JyLie

vuePress-theme-reco JyLie    2017 - 2023
JyLie

Choose mode

  • dark
  • auto
  • light
主页
分类
  • API
  • HTML
  • css
  • vue
  • Linux
  • Docker
  • Webpack
  • WebGL
  • PixiJS
  • Github
  • BOM
  • XML
  • bug
  • ie
  • uniapp
  • IE
  • mysql
  • font
  • bom
  • canvas
  • video
  • html
  • JavaScript
  • js
  • 运算符
  • RegExp
  • 编码
  • MiniApp
  • nginx
  • Tool
  • node.js
  • cat
  • nodejs
  • protocol
  • URL
  • FLOW
  • DNS
  • Protocol
  • python
  • 安全
  • linux
  • shell
  • IDE
  • Packer
  • ViteJS
  • git
  • vendor
  • WebApp
  • WebView
  • Window API
  • webview
  • 规范
标签
时光轴
GitHub
author-avatar

JyLie

74

Article

79

Tag

主页
分类
  • API
  • HTML
  • css
  • vue
  • Linux
  • Docker
  • Webpack
  • WebGL
  • PixiJS
  • Github
  • BOM
  • XML
  • bug
  • ie
  • uniapp
  • IE
  • mysql
  • font
  • bom
  • canvas
  • video
  • html
  • JavaScript
  • js
  • 运算符
  • RegExp
  • 编码
  • MiniApp
  • nginx
  • Tool
  • node.js
  • cat
  • nodejs
  • protocol
  • URL
  • FLOW
  • DNS
  • Protocol
  • python
  • 安全
  • linux
  • shell
  • IDE
  • Packer
  • ViteJS
  • git
  • vendor
  • WebApp
  • WebView
  • Window API
  • webview
  • 规范
标签
时光轴
GitHub
  • 深入浅出Object.create(proto[, propertiesObject])

    • 前言
      • Object.create(proto[, propertiesObject])的使用
        • 区分 new Object() 和 Object.create()
          • 对象创建方式
          • 对象属性描述符
          • 创建空对象时,是否有原型属性
        • 操作原型对象(prototype)
          • Object.setPrototypeOf(obj, prototype)
          • Object.getPrototypeOf()
        • 对象继承
          • 题外话

      深入浅出Object.create(proto[, propertiesObject])

      vuePress-theme-reco JyLie    2017 - 2023

      深入浅出Object.create(proto[, propertiesObject])


      JyLie 2020-10-09 API

      # 前言

      日常开发中经常会碰到创建对象、继承对象的情况,期间需要比较频繁的使用到Object.create()、**Object.assign()**等内置函数。“工欲善其事,必先利其器”,熟练的掌握工具势在必行。接下来会围绕着以下几个知识点来展开:

      • Object.create()的使用
      • 区分 new Object() 和 Object.create()
      • 操作原型对象(prototype)
        • Object.setPrototypeOf()
        • Object.getPrototypeOf()
      • 对象继承
        • 只拷贝自身可枚举属性
        • 拷贝自身可枚举属性、原型上的属性、get /set 属性

      文中的 built-in 指 built-in function and built-in properties

      # Object.create(proto[, propertiesObject])的使用

      • 描述:该方法创建一个新对象,使用现有的对象来提供新创建的对象的proto

      • 语法:

        • proto:必须。新创建对象的原型对象,即通过 Object.create()生成的对象的原型 指向 proto(可以是 null、对象、函数的 prototype 属性)。(注:创建空原型的对象时需传 null , 否则会抛出 TypeError 异常)。
        • propertiesObjec:可选。 添加到新创建对象的可枚举属性(即其自身的属性,而不是原型链上的枚举属性)对象的属性描述符以及相应的属性名称。这些属性对应 Object.defineProperties()的第二个参数。
        • 返回值:一个新对象,带着指定的原型对象和属性。

      因此可以看出 Object.create()的两个参数其实就是,proto 设置原型对象的属性和方法,propertiesObjec 设置新创建对象的属性和方法。而 propertiesObjec 创建属性方法的时候加入了属性可配置的形式,相当于简化并增强对象对象的写法,让其拥有对象字面量和 Object.defineProperties()的特性

      var org = { type: 'human' };
      var persion = Object.create(org);
      var persionSelf = Object.create(org, {
        name: {
          value: 'NvWa',
          configurable: false,
          enumerable: true,
          writable: false,
          // 注意不能同时制定get/set和writable
          // get() {
          //     return '-'
          // },
          // set(val) {
          //     console.log("persionSelf Setting name", val);
          // }
        },
      });
      console.log('first visit: ', persion, Object.getPrototypeOf(persion), persionSelf, Object.getPrototypeOf(persionSelf));
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18

      # 区分 new Object() 和 Object.create()

      可以从对象创建方式;对象属性描述符;创建空对象时,是否有原型属性不同几个方面来探讨

      # 对象创建方式

      • new Object(obj) :通过构造函数的对象, 对象添加的属性 obj 是在自身实例下
      var Obj = new Object(org);
      console.log('create by new Object()', Obj); // { type: 'human' }
      console.log(Obj.__proto__); // {} -> built-in
      console.log(Obj.type); // human
      
      1
      2
      3
      4
      • Object.create(proto[, propertiesObject]) :创建的新对象继承一个对象。 添加的属性 proto 是在原型下,添加的属性 propertiesObject 才在自身实例下
      var ObjCre = Object.create(org, { name: { value: 'JyLie' } });
      console.log('create by Object.create', ObjCre); // {name: 'JyLie'}
      console.log(ObjCre.__proto__); // { type: 'human' }
      console.log(ObjCre.type); // human
      
      1
      2
      3
      4

      # 对象属性描述符

      • 通过 Object.create 第二个参数创建非空对象的属性描述符默认是为 false 的,不可写,不可枚举,不可配置
      console.log(Object.getOwnPropertyDescriptors(ObjCre)); // {type: {configurable: false,enumerable: false,value: "human",writable: false}}
      console.log(ObjCre.name); // JyLie
      ObjCre.name = 'SuperJyLie'; // JyLie。不可修改
      delete ObjCre.name; // false。不可删除
      
      1
      2
      3
      4
      • 构造函数或对象字面量创建的对象属性的描述符默认为 true
      console.log(Object.getOwnPropertyDescriptors(Obj)); // {type: {configurable: true,enumerable: true,value: "human",writable: true}}
      console.log(ObjCre.type); // human
      ObjCre.type = 'SuperJyLie'; // SuperJyLie
      
      1
      2
      3

      # 创建空对象时,是否有原型属性

      • Object.create(null)创建空对象,新对象是没有原型属性的
      Object.create(null); // {} 没有原型对象的空对象。纯空对象。
      
      1
      • 构造函数或对象字面量方法创建空对象时,新对象有原型属性(built-in)
      new Object(); // {} 拥有built-in
      
      1

      因此 Object.create()主要是在创建新对象设置原型对象和对象实例属性的,接下来会讲一下 es6 规范的对象原型操作。

      # 操作原型对象(prototype)

      传统方法是通过给对象的 prototype 上添加属性和方法来实现原型链设置的

      var Person = function() {};
      Person.prototype.age = 18;
      Person.prototype.name = 'JyLie';
      Person.prototype.show = function() {};
      //通过构造函数创建实例
      var traditionPserion = new Person();
      traditionPserion.__proto__ === Person.prototype; // true
      
      1
      2
      3
      4
      5
      6
      7

      通过使用 Object.create(proto[, propertiesObject])则可以直接使用 proto 来设置原型对象,使用 propertiesObject 来设置实例对象属性和方法,使得代码更直观更通俗易懂。除此之外还可以使用 Object.setPrototypeOf()来设置和 Object.getPrototypeOf()读取原型对象。

      # Object.setPrototypeOf(obj, prototype)

      • 描述:用来设置一个对象的 prototype 对象。原型对象 prototype 直接设置在需要设置原型对象的对象 obj 上。

      • 语法:

        • obj:要设置其原型的对象。
        • prototype:该对象的新原型(一个对象 或 null)。
        • 返回值:无
      var setPrototype = { sex: '女' };
      Object.setPrototypeOf(setPrototype, person);
      setPrototype.__proto__ === person; // true
      
      1
      2
      3

      # Object.getPrototypeOf()

      • 描述:用于读取一个对象的原型对象;

      • 语法:

        • obj:要返回其原型的对象。
        • 返回值:给定对象的原型。如果没有继承属性,则返回 null 。
      Object.getPrototypeOf(setPrototype) === person; // true
      
      1

      # 对象继承

      上面分析了有关 Object.create()的使用方法,相信大家对其有个大概的认识了。正所谓实践出真知,接下来会结合 Object.create()来对对象继承做实践。

      下面我来个抛砖引玉,如果你有更好的方法可以相互留言交流学习(*´▽ `)ノノ

      传统的方法定义对象,如果直接通过 Object.assign()继承源对象 org 实例属性和方法生成新对象 obj 时,新对象 obj 不会继承源对象 org 的原型链的属性和方法。并且虽然新对象 obj 继承了源对象 org 的 get/set 属性,但是 get/set 会失去原本的动态监听属性的功能。

      var Person = function() {};
      Person.prototype.age = 18;
      Person.prototype.name = 'JyLie';
      Person.prototype.show = function() {};
      Object.assign(Person.prototype, { objAssign: true }); // 为原型链添加属性objAssign
      //通过构造函数创建实例
      var traditionPserion = new Person();
      traditionPserion.__proto__ === Person.prototype; // true
      // 设置 get/set,测试对象继承的get/set
      Object.defineProperty(traditionPserion, 'getType', {
        enumerable: true, // 设为可枚举,不然 Object.assign 方法会过滤该属性
        get() {
          return 'Could get: ' + this.type;
        },
        set(val) {
          this.type = val;
        },
      });
      
      // 设置情景继承对象实例traditionPserion
      var traditionPserionObjAs = Object.assign({}, traditionPserion);
      // 此时traditionPserionObjAs只会继承traditionPserion的实例属性和set/get属性(但失去get/set的功能),但不能继承traditionPserion的原型属性和方法
      traditionPserionObjAs.show; //  不能拷贝到原型上的方法
      traditionPserionObjAs.type = 'Alient';
      traditionPserionObjAs.getType; //  getType不会动态改变
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25

      那么该怎样才能让新对象拷贝源对象上的原型上的方法呢?可以通过 Object.create 来创建新对象,而原型对象可以通过 Object.getPrototypeOf 获取,实例方法和属性可以通过 Object.getOwnPropertyDescriptors 来获取。

      // 使用Object.getOwnPropertyDescriptors 和 Object.getPrototypeOf() 可以正确拷贝原型对象的方法和属性、实例属性的方法和属性(包括get/set)
      var traditionPserionProto = Object.getPrototypeOf(traditionPserion);
      var traditionPserionOwnPropDesc = Object.getOwnPropertyDescriptors(traditionPserion);
      var traditionPserionByProtoAndOwnPropDesc = Object.create(traditionPserionProto, traditionPserionOwnPropDesc);
      
      1
      2
      3
      4

      因此我们可以得出结论

      • 如果只是拷贝自身可枚举属性,就可以只用 Object.assign 方法
      • (推荐)如果要拷贝 get /set 属性以及原型对象,配合 Object.getPrototypeOf + Object.getOwnPropertyDescriptors + Object.create 使用

      # 题外话

      如果你不需要设置 get/set,那么可以对上面实现小改也可以达到拷贝目的。get/set 失效归根于 Object.assign()的拷贝过滤,而创建阉割版本的对象很多时候不是我们想要的,所以就老老实实使用标准推荐方法吧。

      var traditionPserionProto = Object.getPrototypeOf(traditionPserion);
      var traditionPserionProtoNew = Object.create(traditionPserionProto);
      var traditionPserionByProtoAndObjAss = Object.assign(traditionPserionProtoNew, traditionPserion);
      
      1
      2
      3

      最后,如果哪里有纰漏的请大神多多指教~( ̄ ▽  ̄)~*