美文网首页
js原型链的理解

js原型链的理解

作者: maomizone | 来源:发表于2022-03-28 11:04 被阅读0次

原型链,原型对象,这个原型到底如何理解?我们通过几个问题慢慢展开

【问题1】声明一个函数Person,函数调用前面加个new,它做了什么呢?
 function Person(){}
 
 const zs = new Person()
  1. 创建一个‘空对象’
  2. 这个对象会被关联到Person.prototype指向的对象上(关联对象)
  3. 函数的this被绑定到新对象上
  4. 返回这个新对象
  • 我这里空对象打了引号,是因为它不是纯的空对象,它有关联对象,意思是当我用zs这个对象访问某属性,调用某方法时,它在自身上找不到,会委托关联对象帮它找,关联对象找不到,那么关联对象又会委托它的关联对象去寻找,直到找到最顶层的Object.prototype,如果还找不到,返回undefined,这就是原型链

  • 让我们来验证一下,Object.getPrototypeOf() 方法,就是获取zs的关联对象,我喜欢用关联对象代替原型对象,这更加直观,便于我理解。

console.log(Object.getPrototypeOf(zs) === Person.prototype); // true

很多人看原型链的时候,都是使用了下划线proto来验证

console.log(zs.__proto__ === Person.prototype); // true
【问题2】函数的prototype属性从何而来
function Person(){}
console.log(Object.getOwnPropertyNames(Person)); // ['length', 'name', 'arguments', 'caller', 'prototype']
console.log(Object.getOwnPropertyNames(Person.prototype)); // ['constructor']

可以看到当我声明一个函数时,js自动为这个函数添加了5个属性,函数的prototype属性所指向的对象上又有一个consructor属性

【问题3】对象的下划线proto属性从何而来
console.log(Object.getOwnPropertyNames(zs)); // []

可以看见新创建的对象zs上压根就没有我们期望看到的下划线proto属性,我们看下它的源码

Object.defineProperty(Object.prototype, '__proto__', {
  get() {
    let _thisObj = Object(this);
    return Object.getPrototypeOf(_thisObj);
  },
  set(proto) {
    if (this === undefined || this === null) {
      throw new TypeError();
    }
    if (!isObject(this)) {
      return undefined;
    }
    if (!isObject(proto)) {
      return undefined;
    }
    let status = Reflect.setPrototypeOf(this, proto);
    if (!status) {
      throw new TypeError();
    }
  },
});

所以这个属性其实是Object.prototype的,而且我们通过zs访问此属性时就是通过原型链找到的,调用的是getter方法,其实还是用了Object.getPrototypeOf方法

【问题4】constructor属性?
console.log(zs.constructor === Person.prototype.constructor); // true

以上代码又是我们看原型链时,人家经常会讲到

对象实例的构造函数指向函数原型对象的构造函数啊

刚刚我们已经打印过了,zs其实压根没有constructor 属性,而它的关联对象Person.prototype上有,所以委托关联对象帮它访问到constructor属性,自然这个是相等的。所以大家不要死记硬背,zs上面没有下划线proto和constructor属性!

console.log(Person === Person.prototype.constructor); // true

至于上方代码,这也是js自己干的:函数的prototype属性所指向的对象上面的constructor属性指向函数本身

【问题5】继承?
  • 其实js没有继承这种东西,只是我们喜欢用这种说法帮助我们理解,而且ES6的class语法糖直接用上了extends这个关键字,让我们更加以为有继承这个概念存在。其实js只是帮我们建立了对象之间的关联关系而已!
  • 下面我们用es5的方式写上经典的函数继承
  function Foo(name) {
    this.name = name;
  }
  Foo.prototype.myName = function() {
    return this.name;
  };
  function Bar(name, label) {
    Foo.call( this, name );
    this.label = label;
  }
  // 创建一个新的对象,并设置关联对象为 Foo.prototype,返回这个新对象给Bar.prototype
  Bar.prototype = Object.create( Foo.prototype );
  // 注意!现在没有 Bar.prototype.constructor 了
  // 如果你需要这个属性的话可能需要手动修复一下它
  Bar.prototype.myLabel = function() {
    return this.label;
  };
  var a = new Bar( "a", "obj a" );
  a.myName(); // "a"
  a.myLabel(); // "obj a"

  console.log(Object.getPrototypeOf(a) === Bar.prototype); // true
  console.log(Object.getPrototypeOf(Bar.prototype) === Foo.prototype); // true
  console.log(Object.getPrototypeOf(Foo.prototype) === Object.prototype); // true

这里用到了Object.create ()方法,此方法的含义是创建一个新对象,并给这个新对象设置关联对象,当然我们也可以用另外一种方法

 Object.setPrototypeOf(Bar.prototype, Foo.prototype)

这是修改Bar.prototype关联对象为Foo.prototype,它没有一个创建新对象的过程,可以节省点内存。

  • 就上方代码,我们找一下原型链
  1. a的关联对象为Bar.prototype所指向的对象
  2. Bar.prototype所指向的对象的关联对象为Foo.prototype所指向的对象
  3. Foo.prototype所指向的对象的关联对象为Object.prototype所指向的对象
  • 当我们访问a.toString(),它就会顺着原型链一直找到Object.prototype所指向的对象上去
  console.log(Object.getOwnPropertyNames(Object.prototype)); 
// ['constructor', '__defineGetter__', '__defineSetter__', 'hasOwnProperty', '__lookupGetter__', '__lookupSetter__', 'isPrototypeOf', 'propertyIsEnumerable', 'toString', 'valueOf', '__proto__', 'toLocaleString']

所以不会报错undefined,经过一层层的委托,Object.prototype返回了这个方法的引用给它,现在明白了原型链了吧~

console.log(a.toString === Object.prototype.toString); // true
【问题6】如何创建一个真正的空对象
let o = Object.create(null)
console.log(Object.getPrototypeOf(o)); // null

相关文章

  • 廖雪峰JS小记

    (function(){})() 原型,原型链 浅谈Js原型的理解JS 原型与原型链终极详解 对象 对象:一种无序...

  • 再来看一次JS继承

    原型链继承 理解原型链的概念 用一张图来理解原型链再合适不过了。 总结概括JS红宝书上对原型链的概念:每个函数都有...

  • 2018-01-09 关于javascript原型链的思考 pl

    s 深入理解原型和原型链? 构造函数 理解原型和原型链 new的时候js都干了什么? 一个实现继承的demo 构造...

  • js原型链--js面向对象编程

    简单粗暴地理解js原型链--js面向对象编程 原型链理解起来有点绕了,网上资料也是很多,每次晚上睡不着的时候总喜欢...

  • js继承完全理解

    认识原型链 要理解js的继承原来要先理解原型链,要理解原型链要先清楚下面两个结论: 任何一个构造函数(其实是任何一...

  • 前端资料

    ES6新数据类型 Symbol . js变量提升函数提升 js this js 原型及原型链理解 new做了什么 ...

  • 说说JS原型链

    JS的原型链的分析网上有大把,比如这种: 不过有必要把简单的东西复杂化吗? 其实理解JS原型链只需要理解一个准则,...

  • 原型和原型链

    今天发现一张特别好的图(↑↑↑上图↑↑↑),对原型和原型链的理解特别直观友好。 原型和原型链 基础储备:每个 JS...

  • 原型与新版的类-class

    首先来理解原型原型 === 共用属性可以先看看方姐的几篇文章:什么是JS原型链JS 中 proto 和 proto...

  • js原型-原型链理解

    1、prototype function Person(){ };Person.prototype.name = ...

网友评论

      本文标题:js原型链的理解

      本文链接:https://www.haomeiwen.com/subject/kmzujrtx.html