對(duì)象斋泄、類(lèi)與原型鏈

js是一個(gè)基于對(duì)象的語(yǔ)言,所以本文研究一下js對(duì)象和類(lèi)實(shí)現(xiàn)的過(guò)程和原理镐牺。

對(duì)象的屬性及屬性特性

下面是一個(gè)對(duì)象的各個(gè)部分:

var person = {
  name: "Lily",
  age: 10,
  work: function(){
    console.log("Lily is working...");
  }
};
person.gender = "F"; //可以動(dòng)態(tài)添加屬性
Object.defineProperty(person, "salary", {  //添加屬性
  value: 10000,
  writable: true,  //是否可寫(xiě)炫掐,默認(rèn)false
  enumerable: true,  //是否可枚舉,默認(rèn)false
  configuration: true  //是否可配置任柜,默認(rèn)false;
})卒废;
Object.defineProperties(person, {  //添加多個(gè)屬性
  "father": {
    value: Bob,
    enumerable: true
  },
  "mather": {
    value: Jelly,
    enumerable: true
  }
});
delete person.age; // 刪除屬性
Object.getOwnPropertyDescriptor(person, "father"); //{
value:10000,writable:true,enumerable:true,configuration:true}

是否可寫(xiě)指得是其值是否可修改沛厨;
是否可枚舉指的是其值是否可以被for...in...遍歷到宙地;
是否可配置指的是其可寫(xiě)性逆皮,可枚舉性,可配置性是否可修改秽梅,并且決定該屬性可否被刪除剿牺。

這是一個(gè)普通的對(duì)象和常見(jiàn)操作,不多說(shuō)晒来,下面是一個(gè)具有g(shù)et/set的對(duì)象:

var person = {
  _age: 11,
  get age(){
    return this._age;
  },
  set age(val){
    this._age = val;
  }
};
//如下方法訪(fǎng)問(wèn):
console.log(o.age); //讀
o.age = 30;  //寫(xiě)
console.log(o.age);

對(duì)象的特性

上文,我們只提到了對(duì)象屬性的4個(gè)性質(zhì)荧降,對(duì)象自己其實(shí)也有3個(gè)性質(zhì):

可擴(kuò)展性

可不可擴(kuò)展是指一個(gè)對(duì)象可不可以添加新的屬性;Object.preventExtensions 可以讓這個(gè)對(duì)象變的不可擴(kuò)展辛友。嘗試給一個(gè)不可擴(kuò)展對(duì)象添加新屬性的操作將會(huì)失敗,但不會(huì)有任何提示剪返,(嚴(yán)格模式下會(huì)拋出TypeError異常)废累。Object.preventExtensions只能阻止一個(gè)對(duì)象不能再添加新的自身屬性,仍然可以為該對(duì)象的原型添加屬性随夸,但proto屬性的值也不能修改九默。

var person = {
  name: "Lily",
  age: 10
};    //新創(chuàng)建的對(duì)象默認(rèn)是可擴(kuò)展的
console.log(Object.isExtensible(person));  //true
person.salary = 10000;
console.log(person.salary)  //10000

Object.preventExtensions(person);//將其變?yōu)椴豢蓴U(kuò)展對(duì)象
console.log(Object.isExtensible(person));   //false

person.height = 180; //失敗,不拋出錯(cuò)誤
console.log(person.height); //undefined

person.__proto__.height = 180; //在其原型鏈上添加屬性
console.log(person.height); //180

delete person.age;   //可以刪除已有屬性
console.log(person.age);   //undefined
person.__proto__ = function a(){};  //報(bào)錯(cuò)TypeError: #<Object> is not extensible(…)

function fun(){
  'use strict'
  person.height = 180;  //報(bào)錯(cuò)TypeError: #<Object> is not extensible(…)
}
fun();

Object.defineProperty("height", {
  value: 180
});  //由于函數(shù)內(nèi)部采用嚴(yán)格模式,所以報(bào)錯(cuò)TypeError: #<Object> is not extensible(…)

<small>這里如果不理解__proto__不要緊宾毒,下文會(huì)重點(diǎn)解釋這個(gè)屬性</small>

密封性

如果我們想讓一個(gè)對(duì)象即不可擴(kuò)展驼修,又讓它的所有屬性不可配置,一個(gè)個(gè)修改屬性的configurable太不現(xiàn)實(shí)了诈铛,我們把這樣的對(duì)象叫做密封的(Sealed)乙各。用Object.isSealed()判斷一個(gè)對(duì)象是否密封的,用Object.seal()密封一個(gè)對(duì)象幢竹。 其特性包括不可擴(kuò)展對(duì)象和不可配置屬性的相關(guān)特性耳峦。

var person = {
  name: "Lily",
  age: 10
};    //新創(chuàng)建的對(duì)象默認(rèn)是不密封的
console.log(Object.isSeal(person));  //false

Object.seal(person);//將其變?yōu)槊芊鈱?duì)象
console.log(Object.isSeal(person));   //true

delete person.age;   //無(wú)法刪除已有屬性,失敗焕毫,不報(bào)錯(cuò)蹲坷。但嚴(yán)格模式會(huì)報(bào)錯(cuò)
console.log(person.age);   //undefined
person.__proto__ = function a(){};  //報(bào)錯(cuò)TypeError: #<Object> is not extensible(...)

凍結(jié)性

此時(shí),這個(gè)對(duì)象屬性可能還是可寫(xiě)的邑飒,如果我們想讓一個(gè)對(duì)象的屬性既不可寫(xiě)也不可配置循签,同時(shí)讓該對(duì)象不可擴(kuò)展,那么就需要凍結(jié)這個(gè)對(duì)象疙咸。用Object.freeze()凍結(jié)對(duì)象县匠,用isFrozen()判斷對(duì)象是否被凍結(jié)。由于相比上一個(gè)例子撒轮,僅僅是現(xiàn)有的變得不可寫(xiě)了乞旦,這里就不舉太多例子了。
不過(guò)值得注意的是题山,對(duì)于具有setter的屬性一樣不可寫(xiě)。

var person = {
  name: "Lily",
  _age: 10,
  get age(){
    return this._age;
  },
  set age(val){
    this._age = val;
  }
};    //新創(chuàng)建的對(duì)象默認(rèn)不是凍結(jié)的
console.log(Object.isFrozen(person));  //false

Object.freeze(person);//將其變?yōu)椴豢蓴U(kuò)展對(duì)象
console.log(Object.isExtensible(person));   //false
console.log(Object.isSealed(person));   //true
console.log(Object.isFrozen(person));   //true

console.log(person.name);  //"Lily"
person.name = "Bob"; //失敗玖姑,但不報(bào)錯(cuò)客峭,但嚴(yán)格模式會(huì)報(bào)錯(cuò)舔琅。
console.log(person.name);  //"Lily"

console.log(person.age);   //10
person.age = 30;
console.log(person.age);   //10

深凍結(jié)和淺凍結(jié)

深凍結(jié)和淺凍結(jié)的主要差異出現(xiàn)在可擴(kuò)展性上课蔬,所以你也可以理解為深可擴(kuò)展和淺可擴(kuò)展二跋。我們看一下以下代碼:

var person = {
  addr: {}
}

Object.freeze(person);
person.addr.province = "Guangzhou"; //淺凍結(jié):對(duì)象的屬性對(duì)象可以繼續(xù)擴(kuò)展
console.log(person.addr.province); //"Guangzhou"

為了實(shí)現(xiàn)深凍結(jié)扎即,我們寫(xiě)一個(gè)函數(shù):

var person = {
  name: "nihao",
  addr: {},
  family:{
    slibing:{},
    parents:{}
  }
}

deepFreeze(person);
person.addr.province = "Guangzhou"; //深凍結(jié):對(duì)象的屬性對(duì)象無(wú)法繼續(xù)擴(kuò)展
console.log(person.addr.province); //undefined
person.family.parents.father = "Bob"; //深凍結(jié):對(duì)象的屬性對(duì)象無(wú)法繼續(xù)擴(kuò)展
console.log(person.family.parents.father); //undefined

function deepFreeze(obj){
  Object.freeze(obj);
  for(key in obj){
    if(!obj.hasOwnProperty(key)) continue;
    if(obj[key] !== Object(obj[key])) continue;
    deepFreeze(obj[key]);  //遞歸調(diào)用
  }
}

注意,這里遞歸沒(méi)有判斷鏈表是否成環(huán)刁绒,判斷有環(huán)鏈表是數(shù)據(jù)結(jié)構(gòu)的知識(shí)傻盟,可以使用一組快慢指針實(shí)現(xiàn)娘赴,這里不贅述筝闹。因此在以下情況會(huì)有一個(gè)bug:

function Person(pname, sname){
  this.name = pname || "";
  this.spouse = sname || {};
}
var p1 = new Person("Lily");
var p2 = new Person("Bob", p1);
p1.spouse = p2;
deepFreeze(p1);  //會(huì)陷入無(wú)休止的遞歸。實(shí)際家庭成員關(guān)系更復(fù)雜糊秆,就更糟糕了捉片。RangeError: Maximum call stack size exceeded(…)

構(gòu)造函數(shù)(Constructor)

當(dāng)我們想創(chuàng)建很多個(gè)人的時(shí)候,就不會(huì)像上面這樣一個(gè)一個(gè)寫(xiě)了宗雇。那我們就造一個(gè)工廠(chǎng)赔蒲,用來(lái)生產(chǎn)人(感覺(jué)有點(diǎn)恐怖):

function CreatePerson(pname, page){
  return {
    name: pname,
    age: page
  };
}
p1 = CreatePerson("Lily", 21);
p2 = CreatePerson("Bob", 12);

console.log(p1);  //Object {name: "Lily", age: 21}
console.log(p2);  //Object {name: "Bob", age: 12}

但是這樣寫(xiě)并不符合傳統(tǒng)的編程思路。因此我們需要一個(gè)構(gòu)造函數(shù)(constructor, 也有書(shū)譯為構(gòu)造器)

關(guān)于構(gòu)造函數(shù)和普通函數(shù)的區(qū)別可以看javascript中this詳解中”構(gòu)造函數(shù)中的this"一節(jié)
矾兜。
下面定義一個(gè)構(gòu)造函數(shù):

function Person(pname, page){
  this.name = pname;
  this.age = page;
  this.work = function(){
    console.log(this.name + " is working...");
  };
}
var p1 = new Person("Lily",23);
var p2 = new Person("Lucy", 21);
console.log(p1);
p1.work();
console.log(p2);
p2.work();

不過(guò)這樣寫(xiě)這個(gè)函數(shù)椅寺,每個(gè)對(duì)象都會(huì)包括一部分配并,太浪費(fèi)內(nèi)存溉旋。所以我們會(huì)把公共的部分放在prototype中:

function Person(pname, page){
  this.name = pname;
  this.age = page;
}
Person.prototype.work = function(){
  console.log(this.name + " is working...");
};
var p1 = new Person("Lily",23);
var p2 = new Person("Lucy", 21);
console.log(p1);
p1.work();
console.log(p2);
p2.work();

通過(guò)上面的輸出,我們看到算行,每個(gè)對(duì)象(p1,p2)都包含了一個(gè)__proto__屬性儡陨,這個(gè)是一個(gè)非標(biāo)準(zhǔn)屬性(ES6已經(jīng)把它標(biāo)準(zhǔn)化了),不過(guò)IE中沒(méi)有這個(gè)屬性骗村。

原型鏈與繼承

在學(xué)習(xí)原型鏈之前我們一定要區(qū)分清楚:prototype是構(gòu)造函數(shù)的屬性胚股,而__proto__是對(duì)象的屬性缨伊。當(dāng)然我們依然用代碼說(shuō)話(huà):

再來(lái)一段代碼:

function Person(pname, page){
  this.name = pname;
  this.age = page;
}
Person.prototype.work = function(){
    console.log(this.name + " is working...");
  };

var p = new Person("Lily",23);
console.log(p.constructor);  //function Person(){...}
console.log(p.__proto__);  //Object

console.log(Person.prototype); //Object
console.log(Person.prototype.constructor);  //function Person(){...}

console.log(Person.__proto__);
console.log(Person.constructor);

console.log(Person.__proto__);  //空函數(shù)function(){}
console.log(Person.constructor);   //function Function(){...}

說(shuō)到這里,就有必要學(xué)習(xí)一下原型鏈了党晋。
js沒(méi)有類(lèi)的概念漏益,這樣就不會(huì)有繼承派生和多態(tài)绰疤,但是實(shí)際編程中我們需要這樣的結(jié)構(gòu)轻庆,于是js在發(fā)展過(guò)程中余爆,就從一個(gè)沒(méi)有類(lèi)的語(yǔ)言模擬出來(lái)類(lèi)的效果蛾方,這里靠的就是prototype桩砰。
一個(gè)構(gòu)造函數(shù)的prototype永遠(yuǎn)指向他的父對(duì)象亚隅,這樣這個(gè)構(gòu)造函數(shù)new出來(lái)的對(duì)象就可以訪(fǎng)問(wèn)其父對(duì)象的成員煮纵,實(shí)現(xiàn)了繼承。
如果他的父對(duì)象的prototype又指向一個(gè)父對(duì)象的父對(duì)象砖茸,這樣一層層就構(gòu)成了原型鏈凉夯。如下(用瀏覽器內(nèi)置對(duì)象模型舉例):

console.log(HTMLDocument);
console.log(HTMLDocument.prototype); //HTMLDocument對(duì)象
console.log(HTMLDocument.prototype.constructor.prototype);
console.log(HTMLDocument.prototype.constructor.prototype.constructor.prototype);
console.log(HTMLDocument.prototype.constructor.prototype.constructor.prototype.constructor.prototype);
console.log(HTMLDocument.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype);
/*......*/

如果你覺(jué)得這里應(yīng)該有一張圖震桶,那就看看這個(gè)完整的對(duì)象關(guān)系圖(基于DOM)蹲姐,下文的相關(guān)例子也基于這個(gè)圖:

DOM內(nèi)置類(lèi)

注意:原型鏈?zhǔn)怯懈F的柴墩,他總會(huì)指向Object江咳,然后是null結(jié)束

那么__proto__是什么歼指?一言以蔽之:對(duì)象的__proto__屬性指向該對(duì)象構(gòu)造函數(shù)的原型踩身。如下:

function Person(pname, page){
  this.name = pname;
  this.age = page;
  this.work = function(){
    console.log(this.name + " is working...");
  };
}
var o = new Person("Lily",23);
o.__proto__ === Person.prototype  //true

上面圖中發(fā)現(xiàn),對(duì)象還有一個(gè)constructor屬性峭弟,這個(gè)屬性也很重要拒炎,新創(chuàng)建對(duì)象的constructor指向默認(rèn)對(duì)象的構(gòu)造函數(shù)本身击你,不過(guò)現(xiàn)實(shí)沒(méi)有這么簡(jiǎn)單丁侄。例如:

function Person(){
}
var p1 = new Person();
console.log(p1.constructor);  //function Person(){...}

function Children(){
}
Children.prototype = p1;//這一行和下一行聯(lián)立使用鸿摇,不能忽略下一行
Children.prototype.constructor = Children;  //修正constructor拙吉,這個(gè)不能省略

console.log(Person.prototype.constructor);  //function Person(){...}

console.log(p1.constructor); //function Child(){...}

當(dāng)我們建立了一個(gè)繼承關(guān)系后筷黔,會(huì)使新的構(gòu)造函數(shù)的prototype.constructor指向改構(gòu)造函數(shù)自己椎例,像上面第9行一樣订歪。從第11行也可以看出陌粹,系統(tǒng)本身也是這樣做的福压。這樣就構(gòu)成了下面這個(gè)圖的關(guān)系荆姆,此時(shí)父對(duì)象的constructor指向子構(gòu)造函數(shù)

prototype

<small>注: 圖片來(lái)自網(wǎng)絡(luò)</small>

從上面的這些例子我們不難發(fā)現(xiàn)邮破,函數(shù)也是一個(gè)對(duì)象抒和。因此構(gòu)造函數(shù)也有了constructor和proto屬性摧莽。不過(guò)這里會(huì)比較簡(jiǎn)單:函數(shù)的constructor都是Function(){...};函數(shù)的__proto__都是個(gè)空函數(shù)

其實(shí)在js中除了基本類(lèi)型(null, undefined, String, Number, Boolean, Symbol)以外顿痪,都是對(duì)象征懈÷舭ィ可能你想反駁我:“js中一切都是對(duì)象”。我們看以下幾個(gè)例子:

//以數(shù)字類(lèi)型為例
var a = 1;   //基本類(lèi)型
console.log(a);  //1
console.log(typeof a);  //number
var b = new Number(1);  //對(duì)象類(lèi)型的數(shù)字
console.log(b);   //Number {[[PrimitiveValue]]: 1}
console.log(typeof b);  //object

首先焕窝,js中基本類(lèi)型中除了null和undefined以外的類(lèi)型汗侵,都具有對(duì)象形式。但對(duì)象形式不等于基本類(lèi)型发乔。從上面的輸出結(jié)果來(lái)看栏尚,var a = 1;和var a = new Number(1);完全不是一回事译仗。你或許會(huì)反駁我:"a有方法呀,基本類(lèi)型怎么會(huì)有方法W菥咱圆!",我們?cè)倏聪乱粋€(gè)例子:

var a = 1;
console.log(a.toFixed(2));  //1.00
var b = new Number(1);
console.log(b + 2);  //3

上面的例子看似基本類(lèi)型a有了方法序苏,對(duì)象又可以參與運(yùn)算忱详。實(shí)際上這是隱式類(lèi)型轉(zhuǎn)換的結(jié)果踱阿,上面第二行软舌,瀏覽器自動(dòng)調(diào)用了new Number()把a(bǔ)轉(zhuǎn)換成了對(duì)象佛点,而第四行利用ValueOf()方法把對(duì)象轉(zhuǎn)換成了數(shù)字超营。

既然函數(shù)也是個(gè)對(duì)象不跟,那么我們不僅可以用構(gòu)造函數(shù)new一個(gè)對(duì)象出來(lái)米碰,也可以為它定義私有方法(變量)和靜態(tài)方法

function Person(pname){
  var age = 10; //私有變量吕座,外面訪(fǎng)問(wèn)不到
  function getAge(){  //私有方法吴趴,外面訪(fǎng)問(wèn)不到
    console.log(age);
  }
  this.name = pname;
  this.getInfo = function(){  //公有方法锣枝,也可以定義在prototype中
    console.log(this.name);
    getAge.call(this);  //注意這里的作用域和調(diào)用方式
  };
};
Person.speak = function(){console.log("I am a person");};  //靜態(tài)方法

var p = new Person("Bob");
p.getInfo();    //Bob 10

Person.speak();    //"I am a person"

當(dāng)然實(shí)現(xiàn)簡(jiǎn)單的對(duì)象繼承不用這么復(fù)雜供鸠,可以使用Object.create(obj);返回一個(gè)繼承與obj的對(duì)象回季。對(duì)與Object.create()方法需要考慮一下幾種情況:

var o = {};
var r1 = Object.create(o);  //創(chuàng)建一個(gè)r1繼承于o
var r2 = Object.create(null);  //創(chuàng)建一個(gè)r2繼承于null
var r3 = Object.create(Object);   //創(chuàng)建一個(gè)r3繼承于Object
console.log(r1); //是一個(gè)繼承自o的對(duì)象
console.log(r2); //是一個(gè)空對(duì)象泡一,沒(méi)有__proto__屬性
console.log(r3); //是一個(gè)函數(shù)

有了先前的知識(shí)鼻忠,我們可以寫(xiě)出來(lái)一個(gè)函數(shù)實(shí)現(xiàn)Object.create()

function inherit(o){
  //if(Object.create) return Object.create(o);
  if(o !== Object(o) && o !== null)  throw TypeError("Object prototype may only be an Object or null");
  function newObj(){};
  newObj.prototype = o || {};
  var result = new newObj();
  if(o === null) result.__proto__ = null;
  return result;
}
var obj = {};
console.log(Object.create(obj));
console.log(inherit(obj));
console.log(Object.create(null));
console.log(inherit(null));
console.log(Object.create(Object));
console.log(inherit(Object));

看了這么多,怎么寫(xiě)繼承比較合理塑娇,我們實(shí)現(xiàn)2個(gè)構(gòu)造函數(shù)埋酬,讓Coder繼承Person写妥。比較以下3種方法:

function Person(pname){
  this.name = pname;
}
function Coder(){}

//方法一:共享原型
Coder.prototype = Person.prototype;

//方法二:實(shí)例繼承
Coder.prototype = new Person("Lily");
Coder.prototype.constructor = Coder;

//方法三:本質(zhì)上還是實(shí)例繼承
Coder.prototype = Object.create(Person.prototype);

當(dāng)然還有其他的繼承方法:

//方法4:構(gòu)造繼承
function Person(pname){
  this.name = pname;
}
function Coder(pname){
  Person.apply(this, argument);
}

//方法5:復(fù)制繼承
function Person(pname){
  this.name = pname;
  this.work = function() {...};
}
var coder = deepCopy(new Person()); //拷貝
coder.code = function(){...};  //擴(kuò)展新方法
coder.language = "javascript";  //擴(kuò)展新屬性
coder.work = function() {...};  //重構(gòu)方法

//下面是深拷貝函數(shù)
function deepCopy(obj){
    var obj = obj || {};
    var newObj = {};
    deeply(obj, newObj);

    function deeply(oldOne, newOne){
        for(var prop in oldOne){
            if(!oldOne.hasOwnProperty(prop)) continue;
            if(typeof oldOne[prop] === "object" && oldOne[prop] !== null){
                newOne[prop] = oldOne[prop].constructor === Array ? [] : {};
                deeply(oldOne[prop], newOne[prop]);
            }
            else
                newOne[prop] = oldOne[prop];
        }
    }
    return newObj;
}

既然方法這么多祝峻,我們?cè)撊绾瓦x擇莱找,一張表解釋其中的區(qū)別

--- 共享原型 實(shí)例繼承 構(gòu)造繼承 復(fù)制繼承
原型屬性 繼承 繼承 不繼承 繼承
本地成員 不繼承 繼承 繼承 繼承
子類(lèi)影響父類(lèi) Y N N N
執(zhí)行效率
多繼承 N N Y Y
obj instanceof Parent true true false false

子類(lèi)的修改會(huì)影響父類(lèi)是絕對(duì)不行的轴踱,所以共享原型是不能用的淫僻。在考慮到使用方便,只要不涉及多繼承就用實(shí)例繼承棕所,多繼承中構(gòu)造繼承也好于復(fù)制繼承琳省。

instanceof

instanceof用來(lái)判斷對(duì)象是否某個(gè)構(gòu)造函數(shù)的實(shí)例。這個(gè)東西很簡(jiǎn)單桦他,不僅可以判斷是否直接構(gòu)造函數(shù)實(shí)例快压,還能判斷是否父對(duì)象構(gòu)造函數(shù)的實(shí)例

function Person(){}

var p = new Person();

console.log(p instanceof Person); //true
console.log(p instanceof Object); //true

多態(tài)/重構(gòu)

js的方法名不能相同蔫劣,我們只能模擬實(shí)現(xiàn)類(lèi)似c++一樣的多態(tài)。

編譯時(shí)多態(tài)

注意:這個(gè)名字只是用了強(qiáng)類(lèi)型語(yǔ)言的說(shuō)法鸵隧,js是個(gè)解釋型語(yǔ)言豆瘫,沒(méi)用編譯過(guò)程育灸。

在方法內(nèi)部判斷參數(shù)情況進(jìn)行重載

  1. 參數(shù)數(shù)量不同做不同的事情
//修改字體磅崭,僅用部分屬性舉例:
function changeFont(obj, color, size, style){
  if(arguments.lenght === 4){
    //當(dāng)傳入了參數(shù)為4個(gè)參數(shù)時(shí)候做的事情
    obj.style.fontSize = size;
    obj.style.fontColor = color;
    obj.style.fontStyle = style;
    return;
  }
  if(arguments.length === 2 && typeof arguments[1] === "object"){
    //當(dāng)傳入了參數(shù)為2個(gè)參數(shù)時(shí)候做的事情
    obj.style.fontSize = arguments[1].size || obj.style.fontSize;
    obj.style.fontStyle = arguments[1].style || obj.style.fontStyle;
    obj.style.fontColor = arguments[1].color || obj.style.fontColor;
    return;
  }
  throw TypeError("the font cannot be changed...");
}
  1. 參數(shù)類(lèi)型不同做不同的事情
//構(gòu)造簡(jiǎn)單對(duì)象
function toObject(val){
  if(val === Object(val)) return val;
  if(val == null) throw TypeError("'null' and 'undefined' cannot be an Object...");
  switch(typeof val){
    case "number": return new Number(val);
    case "string": return new String(val);
    case "boolean": return new Boolean(val);
    case "symbol": return new Symbol(val);
    default: throw TypeError("Unknow type inputted...");
  }
}

運(yùn)行時(shí)多態(tài)

java的多態(tài)都是編譯時(shí)多態(tài)砸喻。所以這個(gè)概念是源于c++的,c++利用虛基類(lèi)實(shí)現(xiàn)運(yùn)行過(guò)程中同一段代碼調(diào)用不同的函數(shù)的效果蒋譬。而在js中可以利用函數(shù)傳遞實(shí)現(xiàn)運(yùn)行時(shí)多態(tài)

function demo(fun, obj){
  obj = obj || window;
  fun.call(this);
}

function func(){
  console.log("I'm coding in " + this.lang);
}

var lang = "C++";
var o = {
  lang: "JavaScript",
  func: function(){
    console.log("I'm coding in " + this.lang);
  }
};

demo(func);
demo(o.func);
demo(func, o);

重寫(xiě)

我們都知道子對(duì)象可以重寫(xiě)父對(duì)象中的函數(shù)割岛,這樣子對(duì)象函數(shù)對(duì)在子對(duì)象中替代父對(duì)象的同名函數(shù)。但如果我們希望既在子對(duì)象中重寫(xiě)父類(lèi)函數(shù)犯助,有想使用父類(lèi)同名函數(shù)怎么辦癣漆!分一下幾個(gè)情況討論:

//情況1
function Person(){
  this.doing = function(){
    console.log("I'm working...");
  };
}
function Coder(){
  Person.call(this);
  var ParentDoing = this.doing;
  this.doing = function(){
    console.log("My job is coding...");
    ParentDoing();
  }
}
var coder = new Coder();
coder.doing();  //測(cè)試


//情況2
function Person(){
}
Person.prototype.doing = function(){
  console.log("I'm working...");
};
function Coder(){
  Person.call(this);
  this.doing = function(){
    console.log("My job is coding...");
    Person.prototype.doing.call(this);
  };
}
var coder = new Coder();
coder.doing();  //測(cè)試

//情況3
function Person(){
}
Person.prototype.doing = function(){
  console.log("I'm working...");
};
function Coder(){
}
Coder.prototype = Object.create(Person.prototype);
Coder.prototype.constructor = Coder;
Coder.super = Person.prototype;
Coder.prototype.doing = function(){
  console.log("My job is coding...");
  Coder.super.doing();
};
var coder = new Coder();
coder.doing();  //測(cè)試
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市剂买,隨后出現(xiàn)的幾起案子惠爽,更是在濱河造成了極大的恐慌婚肆,老刑警劉巖讨越,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件崔赌,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)速缨,“玉大人,你說(shuō)我怎么就攤上這事擂橘。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵旭斥,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮作郭,結(jié)果婚禮上咏尝,老公的妹妹穿的比我還像新娘。我一直安慰自己酣倾,他們只是感情好置侍,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著曼追,像睡著了一般。 火紅的嫁衣襯著肌膚如雪篷就。 梳的紋絲不亂的頭發(fā)上窟绷,一...
    開(kāi)封第一講書(shū)人閱讀 51,198評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音辐怕,去河邊找鬼。 笑死罚渐,一個(gè)胖子當(dāng)著我的面吹牛谈息,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播慕爬,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼棺榔,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼设塔!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起跨算,我...
    開(kāi)封第一講書(shū)人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤哪自,失蹤者是張志新(化名)和其女友劉穎撑柔,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體命锄,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鲫构。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖赫模,靈堂內(nèi)的尸體忽然破棺而出廓脆,到底是詐尸還是另有隱情,我是刑警寧澤姐刁,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布壁拉,位于F島的核電站,受9級(jí)特大地震影響柏靶,放射性物質(zhì)發(fā)生泄漏弃理。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一宿礁、第九天 我趴在偏房一處隱蔽的房頂上張望案铺。 院中可真熱鬧,春花似錦梆靖、人聲如沸控汉。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)姑子。三九已至,卻和暖如春测僵,著一層夾襖步出監(jiān)牢的瞬間街佑,已是汗流浹背谢翎。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留沐旨,地道東北人森逮。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像磁携,于是被迫代替她去往敵國(guó)和親褒侧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容