js 原型链与继承整理

141 阅读3分钟

JavaScript高级程序设计解释

继承是OO语言中的一个最为人津津乐道的概念.许多OO语言都支持两种继承方式: 接口继承 和 实现继承 .接口继承只继承方法签名,而实现继承则继承实际的方法.由于js中方法没有签名,在ECMAScript中无法实现接口继承.ECMAScript只支持实现继承,而且其 实现继承 主要是依靠原型链来实现的.

原型和实例的关系

每个构造函数(constructor)都有一个原型对象(prototype),原型对象都包含一个指向构造函数的指针,而实例(instance)都包含一个指向原型对象的内部指针.

什么是原型对象prototype

因为构造函数生产的实例无法共享属性,所以可以通过一个原型对象实现继承共用。

let a = function() {}
a.prototype.say = function() {
    console.log('say hello')
}
let b = new a() 
let c = new a() 
b.say() // say hello
c.say() // say hello

以上构造函数和原型方法不能使用箭头函数,this会指向window而不是对应的构造函数

let a = () => {}
let b = new a() //  a is not a constructor
let a = function() {}
let b = new a()
a.prototype.who = () => {
    console.log(this)
}
b.who() // window
a.prototype.who = function() {
    console.log(this)
}
b.who() // a

什么是原型链?__proto__又有什么用?

一个对象或函数通过__proto__这个属性连接者它所对应父级的原型对象prototype

let a = function() {}
let b = new a() 
b.__proto__ === a.prototype // true
a.__proto__ === Function.prototype // true

通过__proto__一层层往上寻找继承从而形成“原型链”,这里a.prototype.__proto__往上则是Object.prototype,最顶端则是null。还有这里寻找是就近原则,如果在第一层找到对应的属性就不会向上寻找了。

js中每一个对象或函数都有__proto__属性,但是只有函数对象才有prototype属性。

instanceof 可以在继承关系中用来判断一个实例是否属于它的父类型

let a = function() {}
let b = new a() 
b instanceof a // true

详细用法可以参考

developer.mozilla.org/zh-CN/docs/… www.ibm.com/developerwo…

constructor属性

let a = function() {}
let b = new a() 
b.constructor === a // true

使用constructor属性可以查询到实例对象的父级构造方法,但是这个constructor属性是继承过来的

let a = function() {}
a.prototype = {}
let b = new a() 
b.constructor === a // false

这个constructor属性是继承原型对象prototype的,原型对象prototype的constructor属性是指向该构造函数

let a = function() {}
a.prototype.constructor === a // true

看图说话(图片来自 blog.csdn.net/cc188688768…

上图,Foo构造函数new Object()生成一个实例对象f1,这个new Object()过程可以理解成三步:

1.新建了一个空对象f1

2.将空对象的__proto__指向了Foo函数的原型对象prototype

3.将Foo函数中的this指针指向f1,f1有了Foo构造函数中的属性或方法 ,然后Foo函数返回的是对象,直接返回Foo函数中的对象否则返回f1

let f1 = {}
f1.__proto__ = Foo.prototype
let newObj = Foo.apply(f1, arguments) 
return typeof newObj === 'object' ? newObj : f1

Object.create()可以作为对象继承,步骤也可以分为三步:

1.新建了一个空构造函数f1

2.将空构造函数的原型对象prototype指向了Foo函数

3.将空构造函数实例化成新对象

let f1 = function () {};
f1.prototype = Foo;
let newObj = new f1();
return newObj;

new Object()与Object.create()的区别就是:

1.Object.create()不会把构造函数的属性传下来,而new Object()会根据参数赋值

2.Object.create()创建的新对象的原型指向接收的参数对象,new Object() 创建的新对象的原型指向的是 Object.prototype

3.Object.create(null)可以创建一个没有原型prototype的对象,而 new Object() 创建的对象是 Object的实例,原型链指向Object.prototype

let a = Object.create(null) 
let b = new Object()
console.log(a.__proto__) // undefined
console.log(b.__proto__) // Object.prototype