2021-08-08 系统装修,清理 proto
在提到原型链时候,很多人会头疼,这里尝试换个思路进行解释。
注意:修改原型的继承关系是非常敏感的操作,Object.setPrototypeOf 会影响所有访问[[prototype]]的代码,可能会产生严重的性能问题。
因此最佳实践是,另起炉灶 Object.create(xx.prototype),创建新对象并执行原型。
约定前提:
- prototype统一称为原型
- proto 这个行为称为构造函数原型
- 在所有书写
__proto__的地方统一写成Object.getPrototypeOf和规范一致 - 使用 Object.create 替换 Object.setPrototypeOf
- 默认你了解基本概念,只是混沌不清楚。
导引
请看两个demo案例1
直接看demo,可以在浏览器里直接看:
const obj = { a: 1 };console.log(obj.toString()); // [object Object]Object.getProrotypeOf(obj)===Object.prototype// 上述的也可以写成这样Fn.prototype.isPrototypeOf(fn) // true
我们直接创建了一个对象,没有声明toString方法,但是可以直接调用,为何?说明有调用了其他地方的。从哪里来,慢慢解释。
打印 obj,Chrome这样提示,有一个 Object[[Prototype]]的标志(Chrome 低版本会展示 __proto__),视觉差异都是指 getPrototypeOf :
请注意 fn.__proto__是不规范的写法,应该写成 Object.getPrototypeOf(fn)这样的写法。短期来看,用哪个都可以,虽然不规范但是能用。
Object.getPrototypeOf() 方法返回指定对象的原型(内部[[Prototype]]属性的值)。—- MDN
案例2
const Fn = function(){}const fn = new Fn()// 老式写法console.log(fn.__proto__ === Fn.prototype) // 看第一张图console.log(Object.getPrototypeOf(fn)===Fn.prototype)//console.log(fn.__proto__.__proto__ === Object.prototype)//console.log(fn.__proto__.__proto__.__proto__ === null)
第一个关系图出现了
从这图延伸来看,new实例化的过程是怎么样的?补全,但少不了这两个步骤:
const instance = Object.create(null)Object.setPrototypeOf(instance, Fn.prototype)
有一个章节专门描述new。
但这一张图还不能解释为啥 fn.toString() 有结果,这需要去看 Fn.prototype 上有啥

说明,Fn.prototype 最起码是从 Object 上拿的。
Object.getPrototypeOf(Fn.prototype) === Object.prototype // trueObject.getPrototypeOf(Object.prototype) === null

在第一张图的基础上补充。
再看 Fn这个函数,函数本身也可以视为 new Function出来的,因此也可以 Object.getPrototypeOf(Fn)
案例3
var num = 3;console.log(Object.getPrototypeOf(num)===Number.prototype)console.log(Object.getPrototypeOf(Number)===Function.prototype)
同理。
最终版长这样:

原型
请注意,js上的属性可以被覆盖,比如 obj.hasOwnProperty=7,这就覆盖了,同样 Object.prototype.hasOwnProperty也可以被覆盖。
为了明确继承的属性,一般会 Object.create(null)来操作。
原型链
如果访问一个对象上不存在的属性,js会尝试访问原型上的属性、原型的原型以此类推,最终得出undefined对应的原型。这个过程好像一个链条,这是原型链。举例
function Foo(){}Foo.prototype.nn=function(){}var f = new Foo()f.nnn()f.toString()
实例中,f本身没有nnn属性,它会到构造函数的prototype中寻找,一层层向上查找,直到找到。再比如都没有定义的.toString() 一直找到 Object 上找到,这就叫原型链。找不到就是 undefined
如何判断 f.nn 是不是 f自身的属性?hasOwnProperty
for(const item in f){if(f.hasOwnProperty(item){}}
for..in 高级浏览器已经屏蔽了原型中的属性,以防万一还是得使用hasOwnProperty。
看题
new 的背后
请看代码片段:
function MyClass(name = "") {this.name = name;}MyClass.prototype.say = function () {console.log(this.name + " says hi.");};const instance = new MyClass('dog');console.log(instance.say());
通过 new,执行构造函数得到对象实例。继承了构造函数的原型挂载的方法和属性。
new背后的 技术原理:
- 创建一个空对象,作为对象实例
- 让空对象的原型
__proto__,指向构造函数的prototype属性,也就是新对象 Object.setPrototypeOf(obj, Obj.prototype) - 也可以合并操作,直接拿
- 注意,new的时候this指向构造函数,因此让构造函数内部的this指向这个空对象,并执行构造函数的函数逻辑
- 执行逻辑过程中,返回显式返回值
代码 https://gitee.com/xiaoxfa/tech-sharing/blob/master/share-repo/08-prototype/newFun.js
真题
var a = 20;var test = {a: 40,init: () => {console.log(this.a);function go() {console.log(this.a);}go.prototype.a = 50;return go;}};var p = test.init();p();new p()// https://mp.weixin.qq.com/s/0Q9fhHwksHKSrYmB2fb_Wg 年末的大厂前端面试总结(20届双非二本)-终入字节
面向对象,实现继承
说起js的面向对象,继承,始终是个永恒的话题。因为js是基于原型的语言,一直到ES6才正式提出class和extend的概念。
理论上如何基于原型实现继承,可以丢进历史垃圾堆了。但这里还是做个总结,迟早会删除这部分内容。最有价值的意义还是在于深刻理解原型和原型链。
接下来的内容,也就是论证如何实现下面几行内容:
class Animal {constructor(name) {this.name = name;}walk() {console.log(this.name + " 正在行走");}}class Dog extends Animal {constructor(name) {super(name);}walk() {console.log(this.name + " 正在撒欢");}}const animal = new Animal("aa");console.log(animal.walk());// aa 正在行走const dog = new Dog("dog1");console.log(dog.walk());// dog1 正在撒欢
function Animal(name) {this.name = name;}Animal.prototype.walk = function () {console.log(this.name + " 正在行走");};// go onfunction Dog(name) {// 先掉一下,相当于superAnimal.call(this, name);}// 原型迁移,Object.setPrototypeOf(Dog, Animal.prototype);// constructor 修正Object.getPrototypeOf(Dog).constructor = Dog;Dog.prototype.walk = function () {console.log(this.name + " 正在撒欢");};// 试验区const animal = new Animal("aa");console.log(animal.walk());const dog = new Dog("dog1");console.log(dog.walk());
-1 参考资料
- 《JavaScript高级程序设计》第4版 第八章
- 《前端开发核心知识进阶》第七章
- MDN 继承和原型链
- https://juejin.cn/post/6844904200917221389 由浅入深,66条JavaScript面试知识点
