01、object对象(属性 + 方法 Object.assign()、Object.create())

01、属性 -------

01、Object.prototype属性表示object的原型对象
几乎所有的javascript对象都是 Object 实例,一个典型的对象继承了 Object.prototype 的属性(包括方法),尽管这些属性被覆盖。然而,一个 Object 可能是故意创建的,这是不确定的(例如通过Object.create(null) 或者它可能被改变,所以这不再是准确的(例如Object.prototypeOf)

Object原型的改变会传播到所有对象上,除非这些属性和方法被其他对原型链更里层的改动所覆盖。这提供了一个非常强大的,但有潜在危险的机制来覆盖或扩展对象行为。
属性:
----Object.prototype.constructor----所有对象都会从它的原型上继承一个 constructor 属性

    /*json*/
    var obj = {}
    console.log( obj.constructor === Object) // true
    /*object*/
    var obj1 = new Object()
    console.log( obj1.constructor === Object) // true
    /*array*/
    var arr = []
    console.log( arr.constructor === Array) // true
    console.log( arr.constructor === Object) // false
    /*array*/
    var arr1 = new Array()
    console.log( arr1.constructor === Array) // true
    /*Number*/
    var n = new Number(3);
    var n1 = 3
    console.log( n.constructor === Number) // true
    console.log( n.constructor === Array) // false
    console.log( n1.constructor === Number) // true
    /*打印一个对象的构造函数*/
    function One(name) {
        this.name = name
    }
    var one = new One('夜幕小草')
    console.log(one)
    console.log(one.constructor)
    console.log(one.name)

image.png

----Object.prototype.proto-有待研究

02、方法

-- 01、Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。

语法:
Object.assign(target, ...sources)
target:目标对象
sources: 源对象
返回值: 目标对象

描述:如果目标对象中的属性具有相同的键,则属性将被源中的属性覆盖。后来的源的属性将类似地覆盖早先的属性。
Object.assign 方法只会拷贝源对象自身的并且可枚举的属性到目标对象。该方法使用源对象的[[Get]]和目标对象的[[Set]],所以它会调用相关 getter 和 setter。因此,它分配属性,而不仅仅是复制或定义新的属性。如果合并源包含getter,这可能使其不适合将新属性合并到原型中。为了将属性定义(包括其可枚举性)复制到原型,应使用Object.getOwnPropertyDescriptor() 和 Object.defineProperty()。

String类型和Symbol类型的属性都会被拷贝。
在出现错误的情况下,例如,如果属性不可写,会引发TypeError,如果在引发错误之前添加了任何属性,则可以更改target对象。
注意,Object.assign 会跳过那些值为null和nudefined的源对象

/*复制一个对象*/
    var obj2 = {a: 1, b: 2}
    var copy = Object.assign({}, obj2)
    console.log(obj2)
    console.log(copy)
    console.log(obj2 === copy)
image.png

深拷贝问题

深拷贝问题
针对深拷贝,需要使用其他方法,因为 Object.assign 拷贝的是属性值。假如源对象的属性值是一个指向对象的引用,它也只拷贝那个引用值。
    /*深拷贝问题*/
    function test(){
        var ob1 = { a: 0, b: {c: 0}}
        var ob2 = Object.assign({}, ob1)
        console.log(JSON.stringify(ob1)) //{"a":0,"b":{"c":0}}
        console.log(JSON.stringify(ob2)) //{"a":0,"b":{"c":0}}

        ob2.a = 5
        console.log(JSON.stringify(ob1)) //{"a":0,"b":{"c":0}}
        console.log(JSON.stringify(ob2)) //{"a":5,"b":{"c":0}}

        ob2.b.c = 12
        console.log(JSON.stringify(ob1)) //{"a":0,"b":{"c":12}}
        console.log(JSON.stringify(ob2)) //{"a":5,"b":{"c":12}}

        var obb1 = { a: 0, b: {c: 0}}
        var obb3 = JSON.parse(JSON.stringify(obb1))
        obb3.a = 4
        obb3.b.c = 4
        console.log(JSON.stringify(obb1)) //{"a":0,"b":{"c":0}}
        console.log(JSON.stringify(obb3)) //{"a":4,"b":{"c":4}}
    }

    test()

合并对象

    var o1 = {a: 1}
    var o2 = {b: 2}
    var o3 = {c: 3}

    var o123 = Object.assign(o1, o2, o3)
    console.log(o1)  //{a: 1, b: 2, c: 3}  注意目标对象自身也会改变
    console.log(o2)  //{b: 2}
    console.log(o3)  //{c: 3}
    console.log(o123) //{a: 1, b: 2, c: 3}
/*合并具有相同属性的对象*/
var a1 = {a: 1, b: 1, c:1}
var a2 = {b: 2, c:3}
var a3 = {a: 5, c:4}

var a123 = Object.assign({}, a1, a2, a3)
console.log(a123) //{a: 5, b: 2, c: 4} 属性被后续参数中具有相同属性的其他对象覆盖。
拷贝 symbol 类型的属性
/*拷贝 symbol 类型的属性*/
var b1 = { a: 1 };
var b2 = { [Symbol('foo')]: 2 };

var b12 = Object.assign({}, b1, b2);
console.log(b12); // {a: 1, Symbol(foo): 2}
console.log(Object.getOwnPropertySymbols(b12)); // [Symbol(foo)]
    /*继承属性和不可枚举属性是不能拷贝的*/
    var c1 = Object.create({foo: 1}, { // foo 是个继承属性。
        bar: {
            value: 2  // bar 是个不可枚举属性。
        },
        baz: {
            value: 3,
            enumerable: true  // baz 是个自身可枚举属性。
        }
    });

    var c2 = Object.assign({}, c1);
    console.log(c1); // {baz: 3, bar: 2}
    console.log(c1.foo) // 1
    console.log(c2); // { baz: 3 }

    var c4 = Object.create(null)
    c4.k = 5
    console.log(c4) //{k: 5}
    var c5 = Object.assign({}, c4)
    console.log(c5) //{k: 5}
/*原始类型会被包装为对象*/
    var v1 = "abc";
    var v2 = true;
    var v3 = 10;
    var v4 = Symbol("foo")
    var kk = {k: 9}
    var obj = Object.assign({}, v1, null, v2, undefined, v3, v4, kk); 
    // 原始类型会被包装,null 和 undefined 会被忽略。
    // 注意,只有字符串的包装对象才可能有自身可枚举属性。
    console.log(obj); // { "0": "a", "1": "b", "2": "c", "k": 9 }

异常会打断后续拷贝任务

var target = Object.defineProperty({}, "foo", {
    value: 1,
    writable: false
}); // target 的 foo 属性是个只读属性。

Object.assign(target, {bar: 2}, {foo2: 3, foo: 3, foo3: 3}, {baz: 4});
// TypeError: "foo" is read-only
// 注意这个异常是在拷贝第二个源对象的第二个属性时发生的。

console.log(target.bar);  // 2,说明第一个源对象拷贝成功了。
console.log(target.foo2); // 3,说明第二个源对象的第一个属性也拷贝成功了。
console.log(target.foo);  // 1,只读属性不能被覆盖,所以第二个源对象的第二个属性拷贝失败了。
console.log(target.foo3); // undefined,异常之后 assign 方法就退出了,第三个属性是不会被拷贝到的。
console.log(target.baz);  // undefined,第三个源对象更是不会被拷贝到的。

拷贝访问器

var obj = {
  foo: 1,
  get bar() {
    return 2;
  }
};

var copy = Object.assign({}, obj); 
// { foo: 1, bar: 2 }
// copy.bar的值来自obj.bar的getter函数的返回值 
console.log(copy); 

// 下面这个函数会拷贝所有自有属性的属性描述符
function completeAssign(target, ...sources) {
  sources.forEach(source => {
    let descriptors = Object.keys(source).reduce((descriptors, key) => {
      descriptors[key] = Object.getOwnPropertyDescriptor(source, key);
      return descriptors;
    }, {});

    // Object.assign 默认也会拷贝可枚举的Symbols
    Object.getOwnPropertySymbols(source).forEach(sym => {
      let descriptor = Object.getOwnPropertyDescriptor(source, sym);
      if (descriptor.enumerable) {
        descriptors[sym] = descriptor;
      }
    });
    Object.defineProperties(target, descriptors);
  });
  return target;
}

var copy = completeAssign({}, obj);
console.log(copy);
// { foo:1, get bar() { return 2 } }

---02、Object.create() 方法会使用指定的原型对象及其属性去创建一个新的对象。

语法:
Object.create(proto[, propertiesObject])
proto: 新创建对象的原型对象。
propertiesObject: 可选。如果没有指定为undefined,则是要添加到新创建对象的可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性)对象的属性描述符以及相应的属性名称。这些属性对应Object.definedProperties()的第二个参数
返回值:在指定原型对象上添加新属性后的对象
例外:如果proto参数不是null或者一个对象,则抛出一个TypeError异常
// Shape - superclass
function Shape() {
    this.x = 2;
    this.y = 3;
}

// superclass method
Shape.prototype.move = function(x, y) {
    this.x += x;
    this.y += y;
    console.info('Shape moved.');
    console.log(this.x)
    console.log(this.y)
};

// Rectangle - subclass
function Rectangle() {
  Shape.call(this); // call super constructor.  继承了属性
}

var rr = new Rectangle()
console.log(rr.x)  // 2

// subclass extends superclass
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;

var rect = new Rectangle();

console.log('Is rect an instance of Rectangle?',
rect instanceof Rectangle); // true
console.log('Is rect an instance of Shape?',
rect instanceof Shape); // true
rect.move(1, 1); // Outputs, 'Shape moved.' 3 4
如果你希望能继承到多个对象,则可以使用混入的方式。
function MyClass() {
     SuperClass.call(this);
     OtherSuperClass.call(this);
}

// inherit one class
MyClass.prototype = Object.create(SuperClass.prototype);
// mixin another
Object.assign(MyClass.prototype, OtherSuperClass.prototype);
// re-assign constructor
MyClass.prototype.constructor = MyClass;

MyClass.prototype.myMethod = function() {
     // do a thing
};

---使用 Object.create 的 propertyObject参数

var o;

// 创建一个原型为null的空对象
o = Object.create(null);


o = {};
// 以字面量方式创建的空对象就相当于:
o = Object.create(Object.prototype);


o = Object.create(Object.prototype, {
  // foo会成为所创建对象的数据属性
  foo: { 
    writable:true,
    configurable:true,
    value: "hello" 
  },
  // bar会成为所创建对象的访问器属性
  bar: {
    configurable: false,
    get: function() { return 10 },
    set: function(value) {
      console.log("Setting `o.bar` to", value);
    }
  }
});


function Constructor(){}
o = new Constructor();
// 上面的一句就相当于:
o = Object.create(Constructor.prototype);
// 当然,如果在Constructor函数中有一些初始化代码,Object.create不能执行那些代码


// 创建一个以另一个空对象为原型,且拥有一个属性p的对象
o = Object.create({}, { p: { value: 42 } })

// 省略了的属性特性默认为false,所以属性p是不可写,不可枚举,不可配置的:
o.p = 24
o.p
//42

o.q = 12
for (var prop in o) {
   console.log(prop)
}
//"q"

delete o.p
//false

//创建一个可写的,可枚举的,可配置的属性p
o2 = Object.create({}, {
  p: {
    value: 42, 
    writable: true,
    enumerable: true,
    configurable: true 
  } 
});
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342