深入理解JavaScript-原型(二)

简介: 深入理解JavaScript-原型(二)

前文——深入理解JavaScript-原型(一)

中间这段文章发不出来,不知道为什么

image.png

image.png

Object.setPrototypeOf


此方法设置一个特定的对象的原型到另一个对象或 null。ES6 新增方法。语法为:Object.setPrototypeOf(obj, prototype)。具体例子为:

const obja = { a: 1 };
const objb = { b: 1 };
Object.setPrototypeOf(obja, objb);
console.log(obja);

image.png

能看出,我们通过 Object.setPrototypeOf 方法,将 objb 设置为 obja 的原型,打印 obja 时,我们能看到 obja 的隐式原型([[Prototype]])指向 objb

除此之外,还有一个方法能显式继承原型——Object.create


Object.create


它用于创建一个新对象,使用现有的对象作为新创建对象的原型。ES5 新增方法。它的语法是 Object.create(proto, propertiesObject) 。可看案例:


const obja = { a: 1 };
const a = Object.create(obja);
console.log(a);

image.png

笔者在此之前曾写过一篇文章——Object.create,介绍它是如何实现的,其底层就是用到了原型继承


我们对 Object.setPrototypeOf 和 Object.create 进行对比

  • Object.setPrototypeOf,给我两个对象,将其中一个设置为另一个的原型
  • Object.create,给我一个对象,将它作为我所创建的新对象的原型


从发展的角度看,Object.create 是 ES5 出现,但它不满足两个对象设置原型时,ES6 就提供了新的方法——Object.setPrototypeOf。但无论如何,它们都是后来因为个别需求而新增的 API,无法与 JavaScript 一开始采用的隐式继承相媲美


隐式原型继承


这里,我们不妨温习一下 new 实现原理:

  1. 创建一个新对象
  2. 设置该对象的 [[Prototype]] 为构造函数 prototype 属性
  3. 将构造函数内部的 this 赋值给该对象
  4. 执行构造函数的内部代码
  5. 如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象


这就是隐式继承,只要我们使用 new 创建对象,就会执行以上步骤,当然,用对象字面量是有一样的隐式操作


var obj = {}; // === new Object() 对象字面量
var arr = []; // === new Array() 数组字面量
function func() {} //  new Function(); 函数字面量
var str = '123'; // === new Srting('123') 字符串字面量
var bool = true; // === new Boolean(true) 布尔字面量
// ...


JavaScript 提供了几个内置的构造函数,如 Object、Array、Boolean、String、Number 等等,当我们使用 {}[]function 等符号或关键字时,就是在执行与new操作一样的隐式原型继承


我们可以这样说:隐式原型继承的目的是方便开发者更简洁的实现继承


隐式原型继承和显式原型继承的互相转换


无论是隐式原型继承,还是显式原型继承,都是对对象隐式引用的应用。两者之间具有一定的互操作性,也就是说,用其中一个,能实现另一个的部分行为

例如我们用隐式原型继承实现 Object.create,实现上是手写 Object.create 方法,正如我们在 Object.create 中曾实现的那样:


function create(proto) {
  function F() {}
  F.prototype = proto;
  return new F();
}


原理也很简单,创建一个函数,将它的原型赋值为目标对象,在实例化这个函数,返回实例化后的值。new 实例化,相当于实例化的值的 [[Prototype]] 指向了目标对象


function create(proto) {
  function F() {}
  // 创建一个函数,每个函数都有一个 prototype 属性,指向原型对象
  F.prototype = proto;
  // 原本的 prototype 是一个对象,其中有两个属性,一个是 constructor,即构造函数,指向 F;另一个为 [[Prototype]],指向 Object.prototype
  // 现在将它赋值为 proto
  return new F();
  // new的作用是创建空对象,将该对象的原型赋值为另一个构造函数的 prototype 属性,并执行该构造函数
  // 所以 new F() 后的实例的的[[Prototype]]指向 F.prototype,也就是传入的 proto
}


以上,我们就用 new 实现了显式原型继承

那么如何用显式原型继承实现 new (或者对象字面量)呢

我们在手写 new 中已经实现过,这里贴上代码:


function new2(Constructor, ...args) {
  var obj = Object.create(null); // 创建一个对象
  obj.__proto__ = Constructor.prototype; // 将新对象的 [[Prototype]] 属性赋值为构造函数的原型对象
  var result = Constructor.apply(obj.args); // this赋值新对象并初始化
  return typeof result === 'object' ? result : obj; // 非空返回结果
}


这也解释了面试时常考的两个面试题:手写 new 和手写 Object.create,两者一个是隐式原型继承,另一个是显式原型继承,两者间能通过各自特性实现对方方法


总结


如此,我们就明白了原型是什么,原型与原型链的关系等等。而且我们知道 「JavaScript 是基于原型继承的语言」这句话的涵义。


虽然现在原型在面试中已经显得不重要,知乎上曾有过这样的问题——面试一个 5 年的前端,却连原型链也搞不清楚,满口都是 Vue,React 之类的实现,这样的人该用吗?。不懂原型照样开发业务也很正常。笔者觉得这并不奇怪,毕竟前端的前端开发已经从面向对象转向函数式编程


在文末回答下文章开头的问题

Q:原型是什么?

A:给其他对象提供共享属性的对象

Q:为什么要有原型?

A:JavaScript 是基于原型继承的语言,在这里是为了实现继承

Q:prototype 和 __proto__ 有什么区别

A:prototype 是函数特有的属性,每个函数创建时都会自带 prototype 属性,它指向一个对象,这个对象叫做原型对象。而每个对象都有一个 __proto__ 属性,它也指向原型对象。如果说两者有什么关系?那么 子对象的__proto__ === 父对象的 prototype

Q:原型链又是什么

A:每个对象都有 __proto__ 属性,它指向原型对象,原型对象也是对象,也有 __proto__ 属性,并指向它的原型对象,这样一层一层,最终指向 null,这种关系被称为原型链。

Q:原型是如何实现继承的

A:原型继承有四种方法,以是否手动操作为依据分为隐式原型继承和显式原型继承。隐式原型继承在我们开发中占大多数,即对象字面量和 new,即这两种方法语言底层会帮我们实现创建对象、关联原型和属性初始化。显式原型继承分为 Object.create 和 Object.setPrototypeOf,它能主动设置某个对象为另一个对象的原型

Q:原型和原型链的关系如何

A:原型是实现继承的方法,原型链是继承的产物


参考资料


相关文章
|
3月前
|
JavaScript 前端开发
js开发:请解释原型继承和类继承的区别。
JavaScript中的原型继承和类继承用于共享对象属性和方法。原型继承通过原型链实现共享,节省内存,但不支持私有属性。
33 0
|
3月前
|
JavaScript 前端开发 Java
深入JS面向对象(原型-继承)(三)
深入JS面向对象(原型-继承)
44 0
|
3月前
|
JavaScript 前端开发 Java
深入JS面向对象(原型-继承)(一)
深入JS面向对象(原型-继承)
48 0
|
6天前
|
JavaScript 前端开发
如何在JavaScript中实现基于原型的继承机制
【8月更文挑战第14天】如何在JavaScript中实现基于原型的继承机制
13 0
|
5天前
|
JavaScript 前端开发
JavaScript中什么是原型?有什么用?
JavaScript中什么是原型?有什么用?
7 1
|
17天前
|
JavaScript 前端开发 Java
什么是JavaScript原型对象
【8月更文挑战第2天】什么是JavaScript原型对象
39 9
|
3月前
|
JavaScript
JS数组增删方法的原理,使用原型定义
JS数组增删方法的原理,使用原型定义
|
12天前
|
设计模式 JavaScript 前端开发
js对原型和继承的理解
了解JavaScript中原型和继承的概念对于编写优雅高效的代码、理解库和框架的内部机制以及执行高级设计模式都有着重要的意义。
21 0
|
2月前
|
设计模式 JavaScript 前端开发
【JavaScript】深入浅出JavaScript继承机制:解密原型、原型链与面向对象实战攻略
JavaScript的继承机制基于原型链,它定义了对象属性和方法的查找规则。每个对象都有一个原型,通过原型链,对象能访问到构造函数原型上的方法。例如`Animal.prototype`上的`speak`方法可被`Animal`实例访问。原型链的尽头是`Object.prototype`,其`[[Prototype]]`为`null`。继承方式包括原型链继承(通过`Object.create`)、构造函数继承(使用`call`或`apply`)和组合继承(结合两者)。ES6的`class`语法是语法糖,但底层仍基于原型。继承选择应根据需求,理解原型链原理对JavaScript面向对象编程至关重要
47 7
【JavaScript】深入浅出JavaScript继承机制:解密原型、原型链与面向对象实战攻略
|
17天前
|
JavaScript 前端开发
详细讲解!JavaScript原型 !
详细讲解!JavaScript原型 !

热门文章

最新文章