《JavaScript应用程序设计》一一3.4 原型代理

简介:

本节书摘来华章计算机出版社《JavaScript应用程序设计》一书中的第3章,第3.4节,作者:Eric Elliott 更多章节内容可以访问云栖社区“华章计算机”公众号查看。

3.4 原型代理

在JavaScript中,所有对象内部都有一个指向其原型的引用,在获取对象的属性与方法时,JavaScript引擎会首先检索当前对象是否有值,如果没有则去检索其原型,如果还没有,再沿着原型链的上游继续查找,直到抵达原型链的末端,也就是Object对象的原型上。
当创建了一个对象字面量时,会隐式地将其与Object的原型关联起来。或者,你可以通过Object.create()方法构造对象,此方法允许你为对象设置原型。Object.create()方法在ES5规范中才被加入,所以可能你需要自己去实现它。下面是MDN(Mozilla Developer Network)文档中一项对Object.create()方法的实现(http://mzl.la/1pFH3jo)

if (!Object.create) {
  Object.create = function (o) {
    if (arguments.length > 1) {
      throw new Error('Object.create implementation'
      + ' only accepts the first parameter.');
    }
    function F() {}
    F.prototype = o;
    return new F();
  };
}

当你通过new关键字调用构造函数时,构造函数的原型对象会被设置为新实例对象原型的引用。如你所见,Object.create()的模拟方案仅仅是一个快捷方式,它先是创建了一个新的构造函数,随后将传入的对象赋值给构造函数的原型,最后返回由原型生成的实例。下面来看看如何在实际场景中使用这个方案。

var switchProto = {
    isOn: function isOn() {
      return this.state;
    },

    toggle: function toggle() {
      this.state = !this.state;
      return this;
    },

    state: false
  },
  switch1 = Object.create(switchProto),
  switch2 = Object.create(switchProto);

test('Object.create', function () {
  ok(switch1.toggle().isOn(),
    '.toggle() works.'
  );

  ok(!switch2.isOn(),
    'instance safe.'
  );
});

被传入Object.create()方法中的任何对象,都会被视为新实例对象的原型,相信你也发现了,这个原型对象有一些不寻常之处。
注意原型中的state值,更改实例switch1上的state值不会影响实例switch2中的state值。原型上的属性好比是默认值一样,会被实例对象中的同名属性所覆盖。
需要特别留意的是,如果你在实例中对原型上的一些引用类型的变量(如对象或数组)做了修改,那么此项修改会在所有使用该原型的实例上生效,但如果你直接替换了实例中的某个属性值,所产生的修改影响仅限于实例本身。

var switchProto = {
    isOn: function isOn() {
      return this.state;
    },

    toggle: function toggle() {
      this.state = !this.state;
      return this;
    },

    meta: {
      name: 'Light switch'
    },

    state: false
  },
  switch1 = Object.create(switchProto),
  switch2 = Object.create(switchProto);

test('Prototype mutations.', function () {
  switch2.meta.name = 'Breaker switch';

  equal(switch1.meta.name, 'Breaker switch',
    'Object and array mutations are shared.'
  );

  switch2.meta = { name: 'Power switch' };

  equal(switch1.meta.name, 'Breaker switch',
    'Property replacement is instance-specific.'
  );
});

在这个例子中,switchProto对象拥有一个名为meta的属性,当你在实例中对meta的子属性值进行修改时,所有原型链中关联对象的子属性值都会被改写,但如果你将实例中的meta对象直接替换为一个新对象时,改动效果仅仅限于实例本身。
警告: 在JavaScript社区中的大部分开发者看来,将所有属性(不包括方法)都搬至原型上做共享,是一种非常危险的编码反模式,因为实际应用中,多数代码异常事故都源于对共享属性的意外修改。

相关文章
|
JavaScript 前端开发 Java
深入JS面向对象(原型-继承)(一)
深入JS面向对象(原型-继承)
31 0
|
1月前
|
JavaScript 前端开发
js开发:请解释原型继承和类继承的区别。
JavaScript中的原型继承和类继承用于共享对象属性和方法。原型继承利用原型链查找属性,节省内存但不支持私有成员。类继承通过ES6的class和extends实现,支持私有成员但占用更多内存。两者各有优势,适用于不同场景。
19 0
|
2月前
|
API
在vite.config.js 配置代理
在vite.config.js 配置代理
69 2
|
3月前
|
JavaScript 前端开发 定位技术
JavaScript 中如何代理 Set(集合) 和 Map(映射)
JavaScript 中如何代理 Set(集合) 和 Map(映射)
50 0
|
3月前
|
存储 JavaScript 前端开发
构造函数和原型的结合应用:轻松搞定JS的面向对象编程(三)
构造函数和原型的结合应用:轻松搞定JS的面向对象编程
|
3月前
|
设计模式 JavaScript 前端开发
构造函数和原型的结合应用:轻松搞定JS的面向对象编程(一)
构造函数和原型的结合应用:轻松搞定JS的面向对象编程
|
3月前
|
前端开发 JavaScript
JavaScript中的原型和原型链
JavaScript中的原型和原型链
|
3月前
|
JavaScript 前端开发
【面试题】最详尽的 JS 原型与原型链终极详解(一)
【面试题】最详尽的 JS 原型与原型链终极详解(一)
|
3月前
|
JSON JavaScript 前端开发
Webpack【Webpack图片处理、Webpack中proxy代理 、自动清理dist、Webpack优化、JavaScript中的代码检查】(三)-全面详解(学习总结---从入门到深化)(下)
Webpack【Webpack图片处理、Webpack中proxy代理 、自动清理dist、Webpack优化、JavaScript中的代码检查】(三)-全面详解(学习总结---从入门到深化)
49 2
|
3月前
|
存储 JavaScript 前端开发
构造函数和原型的结合应用:轻松搞定JS的面向对象编程(二)
构造函数和原型的结合应用:轻松搞定JS的面向对象编程