理解JavaScript中如何现实继承
知识点
- 什么是prototype、__proto__
- 构造函数、原型对象、实例的关系
- 原型链
- 操作符new
- 继承
什么是prototype、__proto__
Object.prototype可以访问原型对象
Object.prototype的__proto__属性暴露了通过它访问的对象内部[[Prototype]](一个对象或null)
构造函数、原型对象、实例的关系
构造函数Object、Array、String、RegExp这些js内置对象、自定对象就是通过function来定义。我们可以通过new操作符来创建实例。构造函数有一个prototype属性可以访问该构造函数的原型对象,而该原型对象有一个constructor属性,指向构造函数,这里形成一个小闭环。通过实例的__proto__属性我们可以访问到该实例对象构造函数的原型对象
1 2 3
| var arr = new Array(); console.log(Array.prototype.constructor === Array);//true console.log(arr.__proto__ === Array.prototype);//true
|
原型链
我们访问一个对象的属性,会访问该对象,找不到就在该对象的原型对象上找,找不到就在该原型对象的原型对象上找,直到找到,最顶端是null
1 2 3 4 5 6 7 8 9 10 11 12 13
| // 下面的代码演示了js引擎如何访问属性 function getProperty(obj,prop){ if(obj.hasOwnProperty(prop)){ return obj[prop] }else if(obj.__proto__ !== null){ return getProperty(obj.__proto__,prop) }else{ return undefined } } var obj = {name:'jay'} obj.name // jay getProperty(obj,'name')// jay
|
new
像其他面向对象语言一样,使用new来实例化一个列,那么js里操作符new做了什么
- 1.创建了一个实例,该对象的__proto__指向构造函数的原型对象
- 2.初始化该实例,this指向创建的实例
- 3.返回该实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| function New(f){ var obj = {__proto__:f.prototype}; // step 1 return function(){ f.apply(obj,arguments); // step 2 return obj; // step 3 } }
function Dog(name,age){ this.name = name; this.age = age; } Dog.prototype.say = function(){ console.log(this.name,this.age) } var dog1 = new Dog('xiaohei',1); dog1.say();//xiaohei 1 console.log(dog1 instanceof Dog);//true
var dog2 = New(Dog)('xiaobai',2); dog2.say();//xiaobai 2 console.log(dog2 instanceof Dog);//true
|
继承
原型链继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function Animal(name){ this.name = name; this.say = function(){ console.log(this.name,this.age); } }
function Dog(name,age){ this.name = name; this.age = age; } Dog.prototype = new Animal('animal');
var dog = new Dog('xiaohei',1); dog.say();//xiaohei 1 console.log(dog instanceof Animal); // false console.log(dog instanceof Dog); // true
|
dog的构造函数没有say这个方法,但是Dog的原型对象指向了Animal,所以就有say方法了。其实我们自定义的对象都默认继承了Object,也就是prototype指向Object的实例,所以我们可以使用Object的toString方法。
1 2
| function Dog(){} Dog.prototype = new Object();
|
缺点父类的属性被覆盖
构造函数继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function Animal(name,age){ this.name = name; this.age = age; this.say = function(){ console.log(this.name,this.age); } }
function Dog(){ Animal.apply(this,arguments); }
var dog = new Dog('xiaohei',1); dog.say();//xiaohei 1 console.log(dog instanceof Animal); // false console.log(dog instanceof Dog); // true
|
组合继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| function Animal(name,age){ this.name = name; this.age = age; this.say = function(){ console.log(this.name,this.age); } }
function Dog(){ Animal.apply(this,arguments); }
Dog.prototype = new Animal(); Dog.prototype.constructor = Dog;
var dog = new Dog('xiaohei',1); dog.say();//xiaohei 1 console.log(dog instanceof Animal); // true console.log(dog instanceof Dog); // true
|
寄生组合继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| function Animal(name,age){ this.name = name; this.age = age; this.say = function(){ console.log(this.name,this.age); } }
function Dog(){ Animal.apply(this,arguments); }
(function(){ var Super = function(){}; Super.prototype = Animal.prototype; Dog.prototype = new Super(); })();
Dog.prototype.constructor = Dog;
var dog = new Dog('xiaohei',1); dog.say();//xiaohei 1 console.log(dog instanceof Animal); // true console.log(dog instanceof Dog); // true
|
REF