Object的原型拷贝-create、assign、getPrototypeOf 方法的结合

一、实现原型拷贝

1.1、代码

tips:为了体现原型链,写了继承实现的代码,这部分可跳过~

    

         <script>
            /* 创建包含原型链的实验对象obj1-- start */
            function inheritPrototype(subType, superType) {
                var prototype = Object(superType.prototype);
                prototype.constructor = subType;
                subType.prototype = prototype;
            }

            function SuperType(name) {
                this.name = name;
                this.colors = [‘red‘, ‘yellow‘, ‘black‘];
            }

            SuperType.prototype.sayName = function() {
                alert(this.name);
            }

            function SubType(name, age) {
                SuperType.call(this, name);
                this.age = age;
            }

            inheritPrototype(SubType, SuperType);

            SubType.prototype.sagAge = function() {
                alert(this.age);
            };

            var obj1 = new SubType(‘puppy‘, 12);
             /* 完成实验对象obj1的创建  --end */

            /* 原型拷贝-- start */
            var obj2 = Object.getPrototypeOf(obj1);
            var obj3 = Object.assign(Object.create(obj2), obj1);
            /* 原型拷贝-- end */

            console.log(obj1);
            console.log(obj2);
            console.log(obj3);
        </script>

以上代码用于验证原型链的拷贝,代码分析如下。

1.2、代码分析

step1:设置拥有原型链的实例obj1,其属性如下:

其中,自身属性:age、colors、name;原型链属性:sayAge、sayName;可忽略部分为对象的默认原型属性,本实验不考察。

step2:使用 Object.getPrototypeOf() 取得实例对象obj1的原型属性对象obj2,属性如下:

step3:使用 Object.create() 将原型属性对象obj2设置成新对象的原型属性

step4:使用 Object.assign() 实现自身属性的拷贝,将其叠加到拥有原型属性的新对象上,形成obj3。

如上,通过step2-step4的过程,实现了对obj1的原型链拷贝,形成新的对象obj3。

1.3、拷贝代码整合(浅拷贝、深拷贝、原型拷贝)

            function clone(origin) {
                return Object.assign({}, origin);
            }

            function moreClone(origin) {
                let oriProto = Object.getPrototypeOf(origin);
                return Object.assign(Object.create(oriProto), origin);
            }

函数clone为简单复制,得到原对象自身可枚举属性的拷贝;

函数moreClone可拷贝原始对象的继承链及自身可枚举的属性。

  tips:这里所用的拷贝方法为浅拷贝。深拷贝、浅拷贝简单说就是看源、目标的所有属性是否相互影响(自身属性、或者原型属性是否指向同一个地址),如果影响就是浅拷贝,如果不影响则为深拷贝。如果origin内有复杂类型的数据,会使得属性指向相同地址,从而使得源、目标对象之间相互影响。

    

    如上图,对obj1的colors数据插入一个值,数组的length增1;同时,发现obj3的length也增加了1。浅拷贝得证。

    jquery的$.extend实现的深度拷贝可通过for...in实现,也就是其深拷贝是对自身的与继承的可枚举属性进行~其丢掉了原型链。所以,原型拷贝与深拷贝之间有所区别,可根据需要进行选择。在使用原生JS的环境下,可通过for...in来实现深拷贝。

    将obj1深拷贝给obj4,如下:

            let obj4 = {};
            $.extend(true, obj4, obj1);
            console.log(obj4);    

    chrome控制台输出如下:

    

    该结构与通过for...in输出的obj1的结构相同,其丢失了obj1当中的继承的原型属性。(此处的__proto__为对象基础类型的原型属性,而非继承自自类的原型属性)。

    

    对obj1复杂类型colors插入值,obj4不受影响。深拷贝,得证。

    所以,通过for...in可实现深拷贝、Object.create()结合Object.getPrototypeOf()、Object.assign()实现原型拷贝。

    

二、Object方法深入了解

以上用到了对象的三种方法,来实现原型链的拷贝。依照第一节中的实验代码,加深对这些方法的理解:

2.1、Object.getPrototypeOf(obj1)

概念:返回指定对象的prototype(原型);

如1.2 step2中的图可知,其返回的原型为obj1的原型。

2.2、Object.create(proto, [propertiesObject])

概念:是一种新的对象创建的方法,其有两个参数:

第一个proto,为要创建的对象的原型;

第二个propertiesObject,为对象的属性描述符,与Object.definePropertyOf()同。

如图:

上图通过 Object.create() 分别创建 obj1 与 obj2 对应的新对象,从图中可得出以下结论:

  第一个参数:作为新对象的原型对象”__proto__”;

第二个参数:为属性描述符对象。(通过Object.defineProperties(obj, props)的第二个参数了解)

  所以,Object.create(proto, [propertiesObject]) 可创建一个带有原型属性的新对象。

2.3、Object.assign(target, …sources)

概念:将一个或多个源对象自身的所有可枚举属性 复制 到目标对象。

如图:

将obj1复制给一个空对象,可以看到obj1的原型属性未被复制。由于原型属性并非对象自身的属性,未被复制。

三、附加Object.defineProperty(obj, prop, descriptor)

概念:直接在一个对象上定义新的属性或修改现有属性,并返回该对象。

参数:obj,被操作对象;

prop:要定义或修改的属性的名称;

descriptor:将被定义或修改的属性的描述符。

属性的描述符相关描述如下:

看实例:

            var obj = {};
            Object.defineProperty(obj, ‘test‘,  {
                configurable: false,
                enumerable: false,
                get() {
                    return this._value;
                },
                set(value) {
                    this._value = value + ‘ yeap‘;
                    return true;
                }
             });
            console.log(obj.property1) //undefined
            obj.test = 2
            console.log(obj.property1) // 2 yeap    

可以看到,给obj.test赋值时,会先通过属性描述符进行处理;通过set方法,将value处理以后,赋值给_value;当取值时,返回_value的值。

如上,通过设置 set() 和 get() ,可对属性值的存取进行处理。该属性操作方法已广泛用于数据双向绑定的一些MVVM框架中,其中VUE就使用了该方法。其通过Object.defineProperty方法,实现setter和getter,形成依赖追踪,从而在数据被访问或修改时通知变化。

tips:Object.defineProperties(obj, props)是Object.defineProperty(obj, prop, descriptor)的扩展,可一次设置多个属性的描述符。

示例:

            Object.defineProperties(obj, {
                test1: {
                    configurable: false,
                    enumerable: false,
                    get() {
                       return this._value;
                    },
                    set(value) {
                        this._value = value + ‘ yeap‘;
                        return true;
                    }
                },
                test2: {
                    value: ‘cutcut’,
                    writable: true
                }
             });

时间: 2024-08-06 20:25:04

Object的原型拷贝-create、assign、getPrototypeOf 方法的结合的相关文章

[js高手之路]原型对象(prototype)与原型链相关属性与方法详解

一,instanceof: instanceof检测左侧的__proto__原型链上,是否存在右侧的prototype原型. 我在之前的两篇文章 [js高手之路]构造函数的基本特性与优缺点 [js高手之路]一步步图解javascript的原型(prototype)对象,原型链 已经分享过了. function CreateObj(uName) {             this.userName = uName;             this.showUserName = function

JavaScript原型对象的属性和方法

前面介绍了普通对象的原型链结构,下面会先介绍一下原型对象中的属性和方法,然后介绍函数对象的原型链,最后将原型链进行整合一下. 第一部分Object.prototype的属性和方法 1 Object.prototype.toString方法 因为Object.prototype顶级对象中定义了toString方法,所以任何对象都有一个toString方法. 可以用来检测对象的类型,当然只能检测内置对象的类型,如果是自定义构造函数创建的对象的话无法准确检测出来类型. 使用方式:Object.prot

Linq技术三:Linq to Object 和生成数据表的扩展方法

这篇来谈论一下Linq第三个方面的应用:Linq to Object,只要是继承了IEnumerable或IQueryable接口的Object都能使用Linq特性进行操作.在操作过程当中可能很多人都觉得不好调试不能实时地观察结果数据集,想把IQuery的Linq查询语句转换成数据表DataTable,要怎么实现转换呢?来看一下. 先来说一场景解释一下为什么需要用Linq来解决一些问题,能解决一些什么样的问题,相对于SQL,DataTable等一些传统操作方式有哪些优势? 场景:目前主要数据源有

java 数组的 toString 方法和 equals 方法以及 java.lang.Object 对象的 toString 方法和 equals 方法

1 public class Test { 2 public static void main(String[] args) { 3 int[] a = {1, 2, 4, 6}; 4 int[] b = a; 5 int[] c = {1, 2, 4, 6}; 6 7 //下面这个方法打印的是a数组的引用地址 8 System.out.println(a.toString()); 9 //下面这个方法比较的是两个数组的引用是否相等 10 System.out.println("a.equals

【翻译自mos文章】使用asmcmd命令在本地和远程 asm 实例之间 拷贝asm file的方法

使用asmcmd命令在本地和远程 asm 实例之间 拷贝asm file的方法 参考原文: How to Copy asm files between remote ASM instances using ASMCMD command (Doc ID 785580.1) 适用于: Oracle Database - Enterprise Edition - Version 11.1.0.6 to 11.2.0.2 [Release 11.1 to 11.2] Information in thi

在System.Object中定义的三个比较方法有何异同

分析问题 在本书前面的章节中,笔者已经介绍过System.Object中的所有方法.而其中有三个方法被设计用来进行对象的比较,它们是: (1).static bool ReferenceEquals(object A,object B). (2).static bool Equals(object A, object B). (3).virtual bool Equals(object obj). 1.静态方法:ReferenceEquals实现了类型的引用比较,从参数类型可以看出,它不仅可以用

hp警告Creating default object from empty value 问题的解决方法

hp警告Creating default object from empty value 问题的解决方法 解决方法是找到报错的位置然后看哪个变量是没有初始化而直接使用的,将这个变量先实例化一个空类.如: 复制代码 代码如下: $ct = new stdClass(); 修改文件相应代码,如: 复制代码 代码如下: if ( ! isset( $themes[$current_theme] ) ) { delete_option( 'current_theme' ); $current_theme

js中使用使用原型(prototype)定义方法的好处

经常在前端面试或是和其他同行沟通是,在谈到构造在JS定义构造函数的方法是最好使用原型的方式:将方法定义到构造方法的prototype上,这样的好处是,通过该构造函数生成的实例所拥有的方法都是指向一个函数的索引,这样可以节省内存. 当然,这种说法没有任何问题,只是在实现上,并非只有使用prototype的方式才能达到这样的效果,我们可以将方法以函数的形式定义在构造函数之外,然后在构造函数中通过this.method = method的方式,这样生成的实例的方法也都通过索引指向一个函数,具体如下:

&#39;Settings&#39; object has no attribute &#39;TEMPLATE_DEBUG&#39; 的解决方法

找到该Django项目下的settings文件,把 DEBUG = True 改为 DEBUG = False 就可以正常浏览显示了 参考:https://stackoverflow.com/questions/23517531/attributeerror-settings-object-has-no-attribute-root-urlconf-on-heroku 'Settings' object has no attribute 'TEMPLATE_DEBUG' 的解决方法