一、JS 三大公理
网上有一个很复杂的一个原型链图,看起来太绕了,但是其实只要掌握三个公理即可
- 对象.proto===其构造函数.prototype
- Function 函数构造所有的函数
- Object.prototype 是所有对象的(直接或者间接)原型
由此可推出
[1, 2, 3].__proto__ === Array.prototype;
obj.__proto__ === Object.prototype;
Object.__proto__ === Function.prototype;
如果注意细节的小伙伴可能会问,既然所有构造函数(包括 Object 函数)都是由 Function 构造的,那为什么说 Object.prototype 是所有对象的直接或间接原型?不应该是 Function 吗?
二、js 世界构造顺序
回答这个问题之前我们来捋一捋 JS 的构造顺序
1、首先,js 构造的是一个【根对象】,这个根对象的proto指向的是 null
2、这个根对象创建了一个函数原型和一个 Function 函数(此时两者都没有具体名字,姑且称 Function 函数为 F 吧)
3、把 F 的proto和 prototype 都指向同一个地址,也就是函数原型的地址。
4、在浏览器的变量名保存空间中,命名 F 为 Function,从此,函数原型名称为 Function.prototype。又因为 Function 也是一个对象,那么 js 发明者让 Function 的proto指向函数原型,所以也有了 Function 是由 Function 构造而来。
5、由 Function 构建 Object 函数,Object.proto保存的是 Function.prototype 的地址,Object.prototype 保存的是根对象的地址
所以说 Object.prototype 是所有对象的直接或间接原型。
三、new 出实例对象 a 的时候,js 做了什么?
function Fun(name) {
(this.name = name),
(this.say = function () {
return this.name;
});
}
var a = new Fun("hu");
简单来说分为三步,
var a={}
a.__proto__=Fun.prototype
Fun.call(a)
首先我们创建了一个空对象,然后将这个空对象的 proto 指向了 Fun.prototype,然后 this 指向这个对象,运行里面的函数
四、旧方式继承
//父构造函数
function Father(value){
this.value=value
this.name='yiling'
}
//子构造函数
function Son(value){
Father.call(this,value)
}
let obj2=new Son(0)
obj2.value // 0
obj2.name // yiling
上面代码中,在子构造函数中调用父构造函数,并使用 call 函数传入 this,这样子构造函数就会继承父构造函数的方法。
然后通过Son.prototype = Father.prototype
这种方式直接把父构造函数的原型传给子构造函数,但是这种方式有个缺点,那就是当我修改了 Son 的原型时,也会修改父构造函数的原型,因为这种方式是把原型地址赋值给了 Son
Son.prototype=Object.create(Father.prototype)
Son.prototype.constructor = Son
采取 Object.create 就相当于覆盖了原来 Son.prototype 的值,并让 Son.prototype.proto指向父构造函数的原型,由于覆盖原因,为了避免突发的情况发生所以要让 Son.prototype.constructor 指回 Son 构造函数
五、基于 es6 的 class 继承
class Father{
constructor(value){
this.value=value
this.name="qiuyanxi"
}
say(){
console.log(123)}
}
class Son extends Father{ //使用extends关键字来继承
constructor(value,age){
super(value) //这里要使用super方法来继承父构造函数的属性
this.age=age
}
say(){
super.say()}//这里同样要使用super方式来继承父构造函数的属性
}
不过这种方式还产生了新的知识,那就是让 Son.proto === Father
总的来说,ES6 的演化给我们产生不少语法糖,让我们无需过多关注 JS 的设计,不过个人依然认为不管语法怎样变化,JS 原型链的设计模式是不变的,这对于学习这门语言非常有好处