javaScript原型链(__prop__/protopype)

前言

在开始讲原型链之前我们需要了解与面向对象有关的知识,而原型链部分也要求对原型链的有一定了解(如提前看过JavaScript高级程序设计[第四版]的原型链部分)

面向对象

什么是[面向对象]概念?

要明白面向对象,首先要理解什么是面向过程,面向过程是我们一开始接触编程时的编程思路,比如学C语言的时候。

面向过程(Procedure Oriented 简称PO :如C语言):

从名字可以看出它是注重过程的。当解决一个问题的时候,面向过程会把事情拆分成: 一个个函数和数据(用于方法的参数) 。然后按照一定的顺序,执行完这些方法(每个方法看作一个过程),等方法执行完了,事情就搞定了。

面向对象(Object Oriented简称OO :如C++,JAVA等语言):

看名字它是注重对象的。当解决一个问题的时候,面向对象会把事物抽象成对象的概念,就是说这个问题里面有哪些对象,然后给对象赋一些属性和方法,然后让每个对象去执行自己的方法,问题得到解决。

可看: 2分钟让你明白什么是面向对象编程

面向对象的三大特性

封装

封装,就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。比如私有属性/方法,通过程序逻辑来设定将哪些类的属性暴露出去。

继承

继承,指可以让某个类型的对象获得另一个类型的对象的属性的方法。它支持按级分类的概念。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。

多态

多态,是指一个类实例的相同方法在不同情形有不同表现形式。这个概念相对抽象,一句话总结就是说:同一个方法被不同的对象执行会有不同的结果。有很多面向对象的语言的API中都体现了“多态”的特征。就算语言API中没有提供多态的接口,最简单的在js方法里面通过if else判断入参也能达到实现[多态]这个特性。

为什么需要面向对象

很明显,面向对象有降低代码耦合度,提高代码复用性等优势。如JAVA就有很多面向对象的特性的API,如继承,方法重载等。这是API层面实现的面向对象。不仅如此,我们在开发过程中也要尽量遵循面向对象的思路(三大特性以及诸多的程序设计原则)去设计我们的程序。

与原型链何干?

好,说到这里,面向对象和我们的原型链有什么关系呢?结合上面这段话我们大概可以想到,原型链就是JavsScript中对于[继承]特性的API实现。

原型链

我们回过头来看看[继承]的概念:继承,指可以让某个类型的对象获得另一个类型的对象的属性的方法。

简单地理解,就是对象A继承了对象B,对象A可以用对象B的属性方法了。而[原型链]实现了[继承]这一特性。

表现

原型链相关的逻辑说明在网上有非常多的文章。总的来说就是这么一张图。

而原型链本身指的是,一个对象沿着__proto__这个属性往上找,形成一条链路,这个就是原型链。

 

var Foo = function() {};

var f1 = new Foo;

console.log(f1.__proto__ === Foo.prototype) //true

console.log(Foo.__proto__ === Function.prototype) //true

console.log(Foo.prototype.__proto__ === Object.prototype) //true

Object.prototype.__proto__ === null // true

 

 

如何理解原型链?

就从例子中的这个构造函数开始

 

var Foo = function() {};

var f1 = new Foo;

我们使用new操作符创建了一个A的实例,这个时候f1.__proto__ === Foo.prototype 为 true。

也就是说实例的__proto__指向构造函数的prototype

依据这样的一条特性,我们能推导出上图中大部分的指向关系。

在推导之前,我们需要知道一个前提条件:所有被创建的函数都有protoype属性,对象都有__proto__属性函数的prototype是一个对象,这个对象在该函数被创建的伊始就存在,它的constructor属性默认指向该函数。我们开始推导吧!

比如Js内置的引用对象(同时也是函数)Function,所有的被创建的function都是Function的实例。所以Foo.__proto__ === Function.prototype 为 true。

甚至连他自己也是,所以Function.__proto__ === Function.prototype 为 true。

再往上,所有被创建的对象都是Object的实例

这句话有一个例外,就是Object.create()创建出来的对象的__proto__属性并不指向Object.prototype

我们看到Function.prototype.__proto__指向Object.prototype。那么万物的尽头是Object.prototype,它自己也是一个对象,它不能指向它自己吧?于是就把它的__proto__属性赋值为null。

From 原型链 to 继承

我们已经概括完上面这张图究竟是怎么一回事了。得讲讲为什么原型链能实现[继承]了。

 

const a = {};

a.__proto__ === Object.prototype // true

a.a // undefined

a.toString(); // "[object Object]"

这是一个简单的对象,里面啥都没有,a.a 是undefiend是正常的。为啥toString没问题?

这就是原型链的机制,当访问一个对象内的属性的时候,首先先访问这个对象内可遍历的属性。然后发现没有,就去__proto__指向的对象里面找。再没有,就去__proto__.proto__指向的对象里面去找....直到__proto__指向null。那就不找了。

因此,我们可以将父类放在__proto__链的上游,下游就能够继承上游父类的属性和方法。Object则是最上游,因此所有对象都能访问Object.prototype的可遍历方法和属性,包括toString。

补充

new

[[Construct]]
When the [[Construct]] internal method for a Function object F is called with a possibly empty list of arguments, the following steps are taken:
  1. Let obj be a newly created native ECMAScript object.
  2. Set all the internal methods of obj as specified in 8.12.
  3. Set the [[Class]] internal property of obj to "Object".
  4. Set the [[Extensible]] internal property of obj to true.
  5. Let proto be the value of calling the [[Get]] internal property of F with argument "prototype".
  6. If Type( proto) is Object, set the [[Prototype]] internal property of obj to proto.
  7. If Type( proto) is not Object, set the [[Prototype]] internal property of obj to the standard built-in Object prototype object as described in 15.2.4.
  8. Let result be the result of calling the [[Call]] internal property of F, providing obj as the this value and providing the argument list passed into [[Construct]] as args.
  9. If Type( result) is Object then return result.
  10. Return obj.

首先,说明了[[Construct]]这个内置方法(函数)被执行了之后会做这十件事情。这也是new操作符做的事情,也就是说new操作符调用了构造函数F的内置函数[[Construct]]。

第一步:新建一个ECMA标准对象obj

第二步:按照8.12标准设置obj对象里面的所有内置方法

第三步:设置内置属性[[Class]]为 "Object"

第四步:设置内置属性[[Extensible]]为 true

第五步:让obj的属性——__proto__的值为F的属性——prototype的值

第六步:__proto__如果是对象,那就将 __proto__的值 赋值给 obj的内部属性[[Prototype]]。

第七步:__proto__如果不是对象,那就将 (15.2.4标准的)Object.prototype的值 赋值给 obj的内部属性[[Prototype]]。

拓展

  • Instanceof 为什么能判断A是否是B的原型
  • 怎么解决[A 和 B 都继承C, A 能沿着原型链修改父类C的属性/方法]这样的问题?
  • 常见的继承方式?
  • this的指向有什么规则?——this的绑定
    • call() bind() apply()的区别,有兴趣可以了解实现原理
  • 为什么'dfsfds'.length可以执行
  • 面向对象中的'封装',在java中依赖[私有属性]达到目的,js中直到es6+才出现私有属性,而es6+的大部分语法都可以使用es5实现,私有属性的实现思路是什么?
    • 了解私有属性之前,得先了解[闭包]
      • 作用域链

       

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值