原型与原型链
1
- 函数独有属性 prototype ,指向函数的原型对象(XX.prototype)
- 所有对象拥有属性 proto 和 constructor 。函数也是对象的一种。
- JS中有7种基础数据: string, number, bigint, boolean, undefined, symbol, 和null类型。
除此外,其他所有都是Object类型,也就是对象。 - 原型对象(XX.prototype)的constructor属性指向自定义的构造函数,或内置的构造函数,JavaScript中有许多内置构造函数,比如Function(),Object()。
- 除了基础数据类型,其他皆对象,所以构造函数也是对象,对象上可以挂载方法,即静态方法,如:Object.entries,Array.isArray等。
2
prototype
函数独有的属性,从一个函数指向另一个对象,
代表这个对象是这个函数的原型对象,这个对象也是当前函数所创建的实例的原型对象。
prototype设计之初就是为了实现继承,让某一个构造函数实例化的所有对象可以找到公共的方法和属性。
有了prototype我们不需要为每一个实例创建重复的属性方法,而是将属性方法创建在构造函数的原型对象上(prototype)。
proto
__proto__属性是对象(包括函数)独有的。
每个对象都有__proto__属性,从一个对象指向该对象的原型对象(也可以理解为父对象)。
这样,实例就能访问到构造函数原型上的方法和属性了
tips: hasOwnProperty 可以判断某个属性是对象自己的 还是从原型链上继承来的
原型链的查找机制
当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。
如果没有就查找它的原型对象(也就是 __proto__指向的 构造函数的prototype)。
如果还没有就查找原型对象的原型(Object的原型对象 也即Object.prototype)。
依此类推一直找到 Object 为止(null)。
__proto__的意义就在于给查找机制提供一条路线。
constructor
constructor是对象才有的属性,它从一个对象指向一个函数的。(即对象的构造函数)
这就是constructor的作用: 从一个对象指向一个函数,这个函数就是该对象的构造函数。
比如: p1的constructor属性指向了Parent,那么Parent就是p1的构造函数;
Parent的constructor属性指向了Function,那么Function就是Parent的构造函数;
Function函数的构造函数就是本身了,
也就可以说Function是所有函数的根构造函数。
类式继承(classical inheritance)
- 实现本质
用父类的实例,重写子类的原型。 - 核心代码
Son.prototype = new Parent(); - 缺陷
原型属性中的引用类型属性,会被所有实例共享,更改一个子类实例,会影响其他子类。
构造函数式继承
- 实现本质
在子类的构造函数里,调用call/apply来实现继承。 - 核心代码
1 | function Parent(username, password) { |
- 缺陷
没有继承父类原型上的方法。
组合式继承
- 实现本质
类式继承继承方法,构造函数继承属性 - 缺陷
父类的构造函数被调用了两次,显得多余;
js实现继承————寄生组合式继承
什么是继承
继承是指子类拥有父类的属性和方法,使代码可以复用。
ES6的class、extends是一种语法糖,
实现继承,本质仍是基于原型和原型链。实现思路
在子类构造函数内,用call()调用父类的构造函数,并绑定this;(继承的属性没有被创建在原型链上,因此多个子类不会共享同一个属性)
借助中间函数F实现原型链继承,(封装在inherit函数); (继承父类的方法)
在子类的构造函数的原型上定义新方法。
1 |
|
若不使用es5的 Object.create();
inherit方法可如下,
其中: 函数F仅用于桥接,仅创建了一个new F()实例
1 | function inherit(child, parent) { |
ES6 中 class 的继承
ES6 中引入了 class 关键字,通过 extends 关键字实现继承,还可以通过 static 关键字定义类的静态方法。
需要注意的是:class 关键字只是原型的语法糖, JavaScript 继承仍然是基于原型实现的。
tips: 在子类的构造函数里,通过 super 调用父类的构造方法
new 操作符都做了什么
new 操作符用于创建一个给定构造函数的实例对象。
- 在内存中创建一个新对象obj
- 将新对象内部的
__proto__赋值为构造函数的 prototype 属性。 - 将构造函数内部的 this 指向新对象
- 执行构造函数内部的代码(给新对象添加属性)
- 如果构造函数返回非空对象,则返回该对象。否则返回 this
1 | function myNew(Func, ...args) { |
function Person(name, age) {
this.name = name;
this.age = age;
this.eat = function() {
console.log(age + "岁的" + name + "吃饭");
}
}
Person.run = function () {}
Person.prototype.walk = function () {}
let p1 = new Person("lily", 20);
let p2 = new Person("bob", 30);
console.log(p1.eat === p2.eat); // false
console.log(p1.run === p2.run); // true
console.log(p1.walk === p2.walk); // true
false; true; true
new 操作符使构造函数内的 eat 函数(对象),是开辟一个新的空间存放,构造函数内的this指向实例化对象,所以两个实例对象它不共享。
而原型上两个实例对象自然都是同一份,walk 方法相同。
p1.run 和 p2.run 都是 undefined。因为 run 方法只是作为 Person 自己的静态属性,p1 和之后的原型链上是找不到的。
- 本文作者:JSZ
- 本文链接:blog.vampuck.com/2022/06/16/proto/index.html
- 版权声明:本博客所有文章均采用 BY-NC-SA 许可协议,转载请注明出处!