notes
  • notes
  • codes
    • 安卓脚本
    • redis入门指南
    • js 原型链
    • 如何发布npm
    • go字符串
    • redis
    • this指向
    • go1.13
    • go by example
    • hook
    • go指南 - 官网
    • git基本操作
  • vim
  • training
    • 9月19日 ubc讲座
    • zby
      • DAY 3 :硬拉 | ZBY
      • DAY 2:卧推 | ZBY
      • DAY 1:深蹲 | ZBY
      • DAY 4 :计划 | ZBY
    • 汉唐
    • 周旋康复课
  • book notes
    • 思考致富
    • 邓普顿教你逆向投资
    • 阮琦
    • 魔鬼经济学
    • 网络是怎样连接的
    • 好奇心
    • 魔鬼约会学5.0
    • 股票作手回忆录
    • 漫步华尔街
    • 码农翻身
    • 十分钟速成课 - 哲学
    • 魔鬼答疑
    • 大话数据结构
    • 魔鬼约会学笔记
    • 算法图解
  • 狼人杀
  • 图书馆
  • typora
  • imovie
Powered by GitBook
On this page
  • 指向原型,获得继承
  • 实例化
  • 构造函数
  • 构造函数,不也是个实例化的对象么?
  • 总结一下
  • 参考:

Was this helpful?

  1. codes

js 原型链

Previousredis入门指南Next如何发布npm

Last updated 5 years ago

Was this helpful?

你有没有曾疑惑过,为什么当我们声明了一个object,就能用.hasOwnProperty来判断里面有没有这个key?

var obj = {'1':1};
obj.hasOwnProperty(1); // true

这个.hasOwnProperty方法哪里来的呢?是在新建object的时候,每个实例下面都新建这个方法吗?

其实lint的规范是不让用obj.hasOwnProperty的。如果要实例化一个object,一般是用Object.creat(null),这样object就不会继承任何的方法。我们在prototype写方法的时候也不会污染原有的Object原型。毕竟object是所有人的爹,这样做会更加安全吧。

不过本文用object为例来说明继承的问题,可以把它想象成string的.length一样,差不多一个意思

image-20190701165140964

显然不是。 一个obj就写一次方法、占一次内存,那存一个新华字典,岂不是要几千块硬盘了?

既然大家都是obj,就不如在obj的原型上定义各类方法,每次声明的obj都能继承原型上面的函数。

换言之,就算新建了个obj的object实例,obj底下什么方法都还没定义。但是它能顺着原型链一层层原型往上找,找到.hasOwnProperty方法,就可以拿来用了。当然找不到就会返回undefined。

也就是说,原型链的出现是为了继承,把公用的方法抽象到原型上。

指向原型,获得继承

那么这些实例,是怎么找到自己的原型的呢?

每个实例创建的时候,都会附带一个.__proto__的方法,指向自己的原型。

比如我们实例化的obj,它的.__proto__指向的是Object对象

var obj = {};

obj.__proto__
/*
{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}

展开:
  constructor: ƒ Object()
  hasOwnProperty: ƒ hasOwnProperty()
  isPrototypeOf: ƒ isPrototypeOf()
...
*/

可以看到,object的原型除了hasOwnProperty还有toString、valueOf等等。任何实例化的obj都能通过.__proto__找到并使用这些方法。

你可能会问,object的原型也是个对象(object)啊,它有没有原型呢?

obj.__proto__.__proto__ // null

答案是没有了。或者说,它的原型就是null。

所以这一条原型链就很明显了:

实例化

声明obj的过程,其实是个new的过程

var obj = {};

// 相当于
var obj = new Object()

其中的Object()是一个构造函数。它能构造出实例,就被称为“构造函数”。其实本质上就是个函数。

当Object()在new了一个实例之后,除了让实例能通过.__proto__找到原型对象之外,还与它进行了绑定。

  • 原型对象增加一个.constructor属性,指向构造函数

  • 构造函数增加一个.prototype属性,指向原型对象

这下原型对象和构造函数,也能互相找到对方了

构造函数

那么除了Object()能构造出实例之外,还有其他构造函数吗?

多了去了。

最基本的是js的基本数据类型,比如值类型中有:字符串(String)、数字(Number)、布尔(Boolean);引用数据类型中有:对象(Object)、数组(Array)、函数(Function)。

我们平常声明一个数组、函数,都相当于new了一个Array或Funcion。

不过值类型new出来的是都是其对象。比如

var a = '123'

// 用new则不同
var b = new String('123');  //    String {"123"}

其中a.__proto__指向的原型,就是String对象,类似于未定义版的String{}

说到底,构造函数就是个函数,换言之任何函数,都能成为构造函数。 【箭头函数似乎不行,具体原因还不太懂,似乎因为没有constructor?】

举例而言,我们可以新建一个Person()函数,来实例化person对象,让实例化后的每个对象都能sayHi()

var Person = function() {
    this.sayHi = () => console.log('hi')
}

var person1 = new Person() 
var person2 = new Person        // 不传参数就可以不用加括号,效果是一样的

person1.sayHi() // 'hi'
person2.sayHi() // 'hi'

我们还能给两个小人命名

person1.name = 'Tom'
person2.name = 'Jerry'

也可以修改一下person的原型对象,都增加两个方法:

person1.__proto__.sayBye = () => console.log('bye')

Person.prototype.sayTruth = () => console.log('Joey是真的帅!')

所以现在就长这样:

明显person的原型也是个对象啊,可以通过.__proto__打印发现,它的原型就是obj的原型:

因此,Person()在实例化对象的时候,一方面通过Object()实例化了一个object,作为person的原型。另一方面又把实例化后的person1的.__proto__指向这个原型。

构造函数,不也是个实例化的对象么?

  • 函数都是Function实例化出来的

  • 构造函数,其实就是函数

换言之,构造函数也是Function实例化出来的。

var Person = function() {...}

// 相当于
var Person = new Function(...)

既然是实例化,那除了会生成实例化对象外,也会对应生成一个原型对象,和构造函数互指。

之前我们实例化后,new出来的实例化对象,都会把.__proto__指向其对应生成的原型对象。构造函数也是对象,因此也不例外。

【为了避免太乱,只突出了构造函数的继承指向】

其中Function()的继承对象是很奇怪的【棕色的继承箭头】,就是它的原型对象。

可以这么理解:Function()能实例化它自己,所以.__proto__也就指向了自己的原型对象。

总结一下

现在这个图已经很乱了,但有几点可以理清:

  • 构造函数的prototype和其构造出来的原型的constructor,相互指向对方

  • 构造函数 实例化(new)出来的对象,会继承对应的原型。(.__proto__指向原型)

  • 在一个对象上找不到的方法,就会顺着.__proto__,一层层往原型的下面找。如果找到了null都没找到,那就是没这个方法了。

    整个顺着.__proto__向上找的链条,就是原型链。

参考:

image-20190701165402587
image-20190701163342116
image-20190701173917909
image-20190701173248542
image-20190701200035744
image-20190701201022435
image-20190701204233491
image-20190701205359698

一张图理解原型链
prototype和proto
用自己的方式(图)理解constructor、prototype、proto和原型链