有些知识当时实在看不懂的话,可以先暂且放下,留在以后再看也许就能看懂了。
几个月前,抱着《JavaScript 高级程序设计(第三版)》,啃完创建对象,就开始啃起了 继承 ,然而啃完 原型链 就实在是看不下去了,脑子越来越乱,然后就把它扔一边了,继续看后面的。现在利用这个暑假搞懂了这个继承,就把笔记整理一下啦。
原型链(Prototype Chaining)
先看一篇文章,文章作者讲的非常不错,并且还配高清套图哦。lol…
从原文中小摘几句
构造函数通过 prototype 属性访问原型对象
实例对象通过 [[prototype]] 内部属性访问原型对象,浏览器实现了 proto 属性用于实例对象访问原型对象
一切对象都是Object的实例,一切函数都是Function的实例
Object 是构造函数,既然是函数,那么就是Function的实例对象;Function是构造函数,但Function.prototype是对象,既然是对象,那么就是Object的实例对象
确定原型与实例的关系
有两种方法来检测原型与实例的关系:
instanceof :判断该对象是否为另一个对象的实例
instanceof 内部运算机理如下:
functioninstance_of(L, R){//L 表示左表达式,R 表示右表达式 varO = R.prototype;// 取 R 的显示原型 L = L.__proto__; // 取 L 的隐式原型 while(true) { if(L ===null) returnfalse; if(O === L)// 这里重点:当 O 严格等于 L 时,返回 true returntrue; L = L.__proto__; } }
上面代码摘自: JavaScript instanceof 运算符深入剖析
isPrototypeOf() :测试一个对象是否存在于另一个对象的原型链上
这两个方法的不同点请参看: JavaScript isPrototypeOf vs instanceof usage
只利用原型链实现继承
缺点:1. 引用类型值的原型属性会被实例共享; 2. 在创建子类型的实例时,不能向超类型的构造函数中传递参数
functionFather(){ this.name ="father"; this.friends = ['aaa','bbb']; } functionSon(){ } Son.prototype = newFather(); Son.prototype.constructor = Son; vars1 =newSon(); vars2 =newSon(); console.log(s1.name);// father console.log(s2.name);// father s1.name = "son"; console.log(s1.name);// son console.log(s2.name);// father console.log(s1.friends);// ["aaa", "bbb"] console.log(s2.friends);// ["aaa", "bbb"] s1.friends.push('ccc','ddd'); console.log(s1.friends);// ["aaa", "bbb", "ccc", "ddd"] console.log(s2.friends);// ["aaa", "bbb", "ccc", "ddd"]
只利用构造函数实现继承
实现方法:在子类型构造函数的内部调用超类型构造函数(使用 apply() 和 call() 方法)
优点:解决了原型中引用类型属性的问题,并且子类可以向超类中传参
缺点:子类实例无法访问父类(超类)原型中定义的方法,所以函数复用就无从谈起了。
functionFather(name,friends){ this.name = name; this.friends = friends; } Father.prototype.getName = function(){ returnthis.name; }; functionSon(name){ // 注意: 为了确保 Father 构造函数不会重写 Son 构造函数的属性,请将调用 Father 构造函数的代码放在 Son 中定义的属性的前面。 Father.call(this,name,['aaa','bbb']); this.age =22; } vars1 =newSon('son1'); vars2 =newSon('son2'); console.log(s1.name);// son1 console.log(s2.name);// son2 s1.friends.push('ccc','ddd'); console.log(s1.friends);// ["aaa", "bbb", "ccc", "ddd"] console.log(s2.friends);// ["aaa", "bbb"] // 子类实例无法访问父类原型中的方法 s1.getName(); // TypeError: s1.getName is not a function s2.getName(); // TypeError: s2.getName is not a function
组合继承(Combination Inheritance)
实现方法:使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。
functionFather(name,friends){ this.name = name; this.friends = friends; } Father.prototype.money = "100k $"; Father.prototype.getName = function(){ console.log(this.name); }; functionSon(name,age){ // 继承父类的属性 Father.call(this,name,['aaa','bbb']); this.age = age; } // 继承父类原型中的属性和方法 Son.prototype = newFather(); Son.prototype.constructor = Son; Son.prototype.getAge = function(){ console.log(this.age); }; vars1 =newSon('son1',12); s1.friends.push('ccc'); console.log(s1.friends);// ["aaa", "bbb", "ccc"] console.log(s1.money);// 100k $ s1.getName(); // son1 s1.getAge(); // 12 vars2 =newSon('son2',24); console.log(s2.friends);// ["aaa", "bbb"] console.log(s2.money);// 100k $ s2.getName(); // son2 s2.getAge(); // 24