对象原型

相信大家都这样用过 map :let arr = [0, 1, 2]

let doubleArr = arr.map(c => c * 2)

console.log(doubleArr) // 0, 2, 4

不知道你有没有想过, arr 本身并没有设定 map 属性,那为什么可以用 map 这个函数呢?

把它打印出来看看:console.log(arr)

// 0: 0

// 1: 1

// 2: 2

// length: 3

// __proto__: Array(0)

出现了一个名为 __proto__ 的对象,如果再将其展开,就会看到所有 Array 对象可以使用的函数;当然我们也可以在其中找到 map 函数,而这也正是例子中所调用的 arr.map 这个函数:console.log(arr.map === arr.__proto__.map) // true

这里出现的 __proto__ 对象,也就是所谓的 原型对象(Prototype) 。

不同于 Java、C# 等基于类(Class) 的面向对象语言,通过定义类、创建实例、指定继承等方式来传递属性及方法;Javascript 则是个基于原型(Prototype)的对语言 ,通过预先建立出的原型对象,当新对象建立时指定对象的原型要参照哪个原型对象。

而当我们调用对象的属性或方法时,若对象本身没有这个属性或方法,JavaScript 会自动寻找它原型中的方法,这也就是为什么可以直接调用 arr.map 而不会出错的原因。

原型链

你可能已经发现在前面的例子中,__proto__ 对象里仍然有 __proto__ 属性:console.log(arr.__proto__) // Array 的 Prototype

console.log(arr.__proto__.__proto__) // Object 的 Prototype

console.log(arr.__proto__.__proto__.__proto__) // null

在上述机制中,每当对象建立时都会绑定原型,既然对象都有原型,对象原型本身也是对象,自然也不例外;由这个例子中我们可以看出:arr 是数组实例,原型是 Array

arr.__proto__ 是数组的原型,原型是 Object

arr.__proto__.__proto__ 是对象的原型,原型是 null

arr.__proto__.__proto__.__proto__ 是 null,没有任何属性

由于每个对象都有原型,这样就形成了一个关联一个、层层相互依赖的从属关系,我们把它们叫做原型链(Prototype Chain) ;通过这种机制,让对象得以使用原型中的属性和方法,并凭借原型链一层一层的按顺序继承,让对象能拥有原型链上所有原型的功能,这就是 JavaScript 对象背后的运作机制。补充:在 JavaScript 中,几乎每个原型链的末端都会是 Object,并最后指向到 null。

使用原型

说了这么多,该来点代码了,接下来就来练习一下原型的建立、设定及修改吧。

先来创建一个新的对象构造函数:function Person(name) {

this.name = name

}Person.prototype.hello = function () {

console.log(`Hello ${this.name}.`)

}let gary = new Person('Gary')

gary.hello() // Hello Gary.Object.getPrototypeOf(gary) // {hello: ƒ, constructor: ƒ}

上面的例子创建了一个简单的对象构造函数 Person(),并在构造函数中设定对象属性。对象的方法中,由于方法不需要让每个对象都独自拥有一份,以避免造成冗余的内存消耗,应该要像前面 Array.prototype.map 的例子那样把对象的方法设定给原型对象(Person.prototype),让这个构造函数创建出来的对象都可以共用这些方法。最后建立一个新的 Person 对象,并通过 getPrototypeOf(obj) 获取新产生对象的原型。Q:为什么不直接用 __proto__ 获取原型对象?

A:因为虽然 __proto__ 被几乎所有的浏览器支持,但它是非标准属性;通过 getPrototypeOf 取得对象的原型是正确的方法。

提醒:Person.prototype 不是 Person 的原型,而是构造函数执行后所建立的新对象的原型;千万不要把构造函数的 prototype 属性与对象的原型搞混!

原型继承

接着再创建新的对象原型,并继承 Person:function Engineer(name, skill) {

Person.call(this, name)

this.skill = skill

}

Engineer.prototype = Object.create(Person.prototype)

Engineer.prototype.constructor = Engineerlet alice = new Engineer('Alice', 'JavaScript')

alice.hello() // Hello Alice.

console.log(alice.skill) // JavaScriptObject.getPrototypeOf(alice)

// Person {constructor: ƒ}

这里建立了新的对象 Engineer 的原型,并通过 Engineer.prototype 的指定,让它的原型继承自 Person.prototype,最后再重新设定 Engineer.prototype.constructor,让构造函数重新指回自己;这样就完成了最基本的原型继承。Q:为什么需要重新设定 constructor?

A:这边功过 Object.create 复制了 Person.prototype 的全部属性,连同 constructor 属性都会被覆盖掉,如果 constructor 属性错误,在做 instanceof 判断时会产生错误的结果;因此这边设定继承时需要再次将 constructor 重新指定回构造函数本身。

修改原型

原型的引用、继承是直接参照到原型对象上,并非是在每个对象都复制一份原型;因此可以利用这个特性,在原型上增加自定义的属性和方法,让所有该类型的对象都能得到新方法;许多针对旧版浏览器的 Polyfill 就是这样实现的。

例如我们在写 Vue 项目的时候,可能都有做过类似的操作,把共用性高的属性方法放到 Vue.prototype 中:Object.defineProperty(Vue.prototype, '$date', { value: dateTimeFormat })

// 之后就可以这样用

vm.$date(dateObj)

这样的确很方便,但也要提醒开大家,当我们在做原型修改的时候要特别小心。接着刚才的例子,如果尝试对 Person 原型中的方法做个修改:Person.prototype.hello = function () {

console.log(`Bye ${this.name}.`)

}gary.hello() // Bye Gary.

alice.hello() // Bye Alice.

如结果所示,当对象原型做修改时,所有原型链上有这个原型的对象,通通都会受到影响,不管对象是在修改前还是修改后创建的。

建议大家除非是 Polyfill,否则应该要极力避免对原生对象的原型进行修改,防止造成可能的意外结果。

ES6 的 Class

看完前面这一大段,是不是觉得心很累?别担心,从 ES6 开始添加了 Class 语法糖,使开发者体验提升了很多。下面把前面的例子用 Class 重构一下:class Person {

constructor (name){

this.name = name

}

// 方法会自动放到 Person.prototype

hello() {

console.log(`Hello ${this.name}.`)

}

}class Engineer extends Person {

constructor (name, skill){

super(name) // 调用 Person 的构造函数

this.skill = skill

}

}let alice = new Engineer('Alice', 'JavaScript')

alice.hello() // Hello Alice.Object.getPrototypeOf(alice)

// Person {constructor: ƒ}

很方便,同样的功能,代码的可读性却提高了不少,繁琐的设定也都能交给语法自动帮你完成。不过方便的语法背后,底层仍然是对象原型及原型链。

总结

以上是 JavaScript 中关于对象原型的说明,希望能帮你理解对象原型,在这个什么都是对象的语言中,充分理解并掌握对象原型,是成为专业码农必须要突破的关卡之一。

java 原型链_什么是原型链相关推荐

  1. 编写区块链_编写由区块链驱动的在线社区的综合指南

    编写区块链 by Sandeep Panda 通过Sandeep Panda 编写由区块链驱动的在线社区的综合指南 (A comprehensive guide to coding a blockch ...

  2. js原型和原型链_初识JS原型/原型链/原型继承

    本文思路: 原型->原型链->原型继承->基于原型的继承/ES6中的继承->__proto__与prototype 首先我们知道JS中有对象,比如: var 但是在我们没有对这 ...

  3. java 复印件效果_简历复印—原型模式

    <大话设计模式>书中描述原型(Prototype)模式: 原型模式(Prototype):用用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 原型模式(Prototype ...

  4. python继承方式是基于原型吗_[译] 为什么原型继承很重要

    五天之前我写了一个关于ES6标准中Class的文章.在里面我介绍了如何用现有的Javascript来模拟类并且介绍了ES6中类的用法,其实它只是一个语法糖.感谢Om Shakar以及Javascrip ...

  5. 以太坊区块链_以太坊区块链搭建与使用(一)-私有链

    步骤 一.下载go语言,并配置环境变量 //以太坊源代码依赖的编译与运行环境 二.通过git clone以太坊源码(go-ethereum),并编译 一.go安装 step1:下载 官方(一般打不开) ...

  6. 设计模式 原型模式_设计模式:原型

    设计模式 原型模式 创新设计模式之一是原型设计模式 . 尽管原型是创造模式,但它在概念上与其他模式有所区别. 我的意思是原型在某种意义上创造了自己. 我将在下面解释. 原型模式的所有魔力都基于Java ...

  7. 什么是智能合约 区块链_什么是区块链智能合约?

    什么是智能合约 区块链 关于区块链智能合约的第一件事是它们不是智能合约,或者不是区块链上的合约. 实际上,它们的名字是奇异的. 1 ,让我们将按照相反的顺序这些问题,我们应该搞清楚一个聪明的合同实际上 ...

  8. 区块链为什么叫区块链_什么是区块链?

    区块链为什么叫区块链 世界和它的狗为区块链(及相关技术,这是另一篇文章)疯狂了. 在过去的两年中,这项技术大肆宣传过山车,其中包括同样疯狂的估值,技术建议,媒体曝光,监管噩梦,欺诈和未实现的梦想. 但 ...

  9. hb哈勃公链_哈勃公链:打造一站式聚合支付平台

    哈勃智能支付 Hubble Chain (哈勃公链)旨在通过全球先进的底层公链技术.智能合约.稳定的加密资产体系,搭建一个低门槛.安全.便捷的去中心化全球跨境支付系统,致力于为全球DeFi新金融服务带 ...

最新文章

  1. cffi java_atomiclong-使用CFFI的原子长类型。-David Reid
  2. python的速度问题_python编程如何提升速度篇
  3. JavaScriptjQuery.事件流
  4. 如何从S4HANA的销售订单找到对应的生产订单
  5. 电脑维修的十大原则,你都认同吗?
  6. Mobius 一个运行在 .NET Core 上的 .NET 运行时
  7. 深入react技术栈(10):受控组件和非受控组件
  8. python用链接表实现栈_python 用链表实现栈(计算器1)
  9. Linux服务器数据备份恢复策略
  10. 数学表达式基础——1 基本符号与术语
  11. 蜜蜂路线图c语言思路,打蜜蜂(c语言)(Hit the bee (C language)).doc
  12. AOP技术介绍--(AOP技术基础)
  13. 查看电脑可支持最大内存容量的方法
  14. Github常用英文翻译
  15. ENVI将高程数据拼接并转换为.dem或.dat_bil格式——以GDEM数据为例
  16. Uipath学习(1):Uipath变量及数据类型
  17. Selenium2相关知识最全总结
  18. PHP Web应用开发 -用PHP实现简单的个人博客网站
  19. 用Matlab仿真手写绘图板上的电压分布
  20. 浅谈 iOS Device ID 的修改

热门文章

  1. CentOS安装教程
  2. ie8兼容性视图对ajax,ie8的兼容性视图设置在哪
  3. html背景图片在底部,CSS兑现固定DIV层背景图片且底部显示
  4. 后台管理系统 添加emoji表情
  5. Pyramid pooling module
  6. mysql 张三的语文成绩_mysql的增删改查(列子)
  7. 虚拟机Failed to lock the file 和Reason: The file is too large错误的解决方法
  8. 映射网络驱动器硬盘显示错误
  9. ArcGIS ModelBuilder 迭代器参数传入字段计算器方法
  10. Android权限的介绍