Web前端之路让前端飞前端开发那些事

复制对象 | 属性描述符 | 不变性 | 存在性

2017-09-13  本文已影响32人  姚屹晨

一.对象

1.对象如何复制?

①方法一:巧妙地使用JSON

function test(){
    return 'yyc';
}
var obj1 = {
    age: 21
};
var arr = ['G','e','r','g'];
var anotherArray = [];
var obj = {
    a: 2,
    b: obj1,
    c: arr,
    d: test
};
JSON.parse(JSON.stringify(obj));
>>>{a: 2, b: {…}, c: Array(4)}

②方法二:ES6新定义了一种方法来实现浅复制,它的名字叫Object.assign()方法

var newObj = Object.assign({},obj);
newObj;
>>>{a: 2, b: {…}, c: Array(4), d: ƒ}

③浅复制是什么?

浅复制是对对象地址的复制。通过上面的Object.assign()方法的返回对象可知,复制得到的新对象中a的值会直接复制obj对象中相应的值,也就是2,但是新对象中bcd三个属性其实只是三个引用,它们和obj对象中bcd引用的对象是一样的。

④既然有浅复制,那应该也有深复制把?它是什么?

深复制是开辟新的栈,两个对象对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性。

json100.png age21.png
2.属性描述符

Object.getOwnPropertyDescriptor()

descriptor.png

var person = {
    age: 21
};
Object.defineProperty(person,'age',{
    configurable: false,
    writable: true,
    enumerable: true,
    value: 100
});
person.age;
>>>100

configurableWritable.png
var person = {
    age: 21
};
Object.defineProperty(person,'age',{
    configurable: false,
    writable: true,
    enumerable: true,
    value: 100
});
person.age = 66;
person.age;
>>>66

Object.defineProperty(person,'age',{
    configurable: true,
    writable: true,
    enumerable: true,
    value: 100
});
>>>Uncaught TypeError: Cannot redefine property: age

④一个细节:即使属性是configurable: false,我们还是可以把writable的状态由true改为false,但无法由false改为true

var person = {
    age: 21
};
Object.defineProperty(person,'age',{
    configurable: false,
    writable: true,
    enumerable: true,
    value: 100
});
person.age;
>>>100

Object.defineProperty(person,'age',{
    configurable: false,
    writable: false,
    enumerable: true,
    value: 100
});
person.age;
>>>100

person.age = 66;
person.age;
>>>100
var person = {
    age: 21
};
Object.defineProperty(person,'age',{
    configurable: false,
    writable: false,
    enumerable: true,
    value: 100
});
person.age;
>>>100

Object.defineProperty(person,'age',{
    configurable: false,
    writable: true,
    enumerable: true,
    value: 100
});
person.age;
>>>Uncaught TypeError: Cannot redefine property: age
writableTrueFalse.png

⑤除了无法更改配置,configurable: false还会禁止删除delete这个属性。

function run(){
    return 'l like running';
}
var person = {
    age: 21,
    sport: run
};
person.sport();
>>>"l like running"

Object.defineProperty(person,'sport',{
    configurable: false,
    enumerable: true,
    writable: true
});
delete person.sport;
person.sport();
>>>"l like running"
3.不变性

①有时候你会希望属性或对象是不可改变的,在ES5中有很多方法可以做到这一点。但是这些方法创建的都是浅不变性

②什么是浅不变性?

③方法一:对象常量

var person = {};
Object.defineProperty(person,'favorite_color',{
    value: 'green',
    configurable: false,
    writable: false
});

④方法二:禁止扩展

如果你想禁止一个对象添加新属性并且保留已有属性,可以使用Object.preventExtensions()

function run(){
    return 'l like running';
}
var person = {
    sport: run,
    age: 21
};
Object.preventExtensions(person);
person.name = 'Gerg';
person.name;
>>>undefined
密封冻结.png

⑤密封:Object.seal()

//不能添加新属性
function run(){
    return 'l like running';
}
var person = {
    sport: run,
    age: 21
};
Object.seal(person);
person.name = 'Gerg';
person.name;
>>>undefined
//不能重新配置现有属性
function run(){
    return 'l like running';
}
var person = {
    sport: run,
    age: 21
};
Object.seal(person);
Object.defineProperty(person,'age',{
    configurable: true
});
Object.defineProperty(person,'age',{
    configurable: false
});
>>>Uncaught TypeError: Cannot redefine property: age
//不能删除任何现有属性
function run(){
    return 'l like running';
}
var person = {
    sport: run,
    age: 21
};
Object.seal(person);
delete person.age;
>>>false

person.age;
>>>21
//不过还能修改现有属性的值喔
function run(){
    return 'l like running';
}
var person = {
    sport: run,
    age: 21
};
Object.seal(person);
person.age = 100;
person.age;
>>>100

⑥冻结Object.freeze()

function run(){
    return 'l like running';
}
var person = {
    sport: run,
    age: 21
};
Object.freeze(person);
person.age = 100;
person.age;
>>>21
4.[[Get]]
var person = {
    name: 'Gerg'
};
person.name;
>>>"Gerg"

person.name是一次属性访问,在person对象中实际是使用了[[Get]]操作。

②对象默认的内置[[Get]]操作首先在对象中查找是否拥有同名的属性,如果找到就返回该属性的值。

③如果没有找到同名的属性,按照[[Get]]算法的定义会执行另一种非常重要的行为:遍历可能存在的[[Prototype]]链,也就是原型链。

④最后如果在原型链上也没找到同名属性,那么[[Get]]]操作会返回undefined

⑤细节:访问属性和访问变量是不同滴!(前提:当前词法作用域中不存在的属性和变量)。

//访问属性
var person = {
    name: 'Gerg'
};
person.city;
>>>undefined
//访问变量
z;
>>>Uncaught ReferenceError: z is not defined
5.[[Put]]

①既然有获取属性值的[[Get]],那么也应该有对属性值的设置呀,也就是[[Put]]

②如果已经存在了这个属性,[[Put]]算法大致会检查以下内容:

6.GetterSetter

①对象默认的[[Put]][[Get]]操作分别可以控制属性值的设置和获取。

getter是什么?

③那setter又是什么?

④那这两个玩意有什么用?

⑤前面提到的访问描述符是什么东西?

属性描述符访问描述符.png

var person = {
    get name(){
        return 'Gerg';
    }
};
person.name;
>>>"Gerg"

Object.defineProperty(person,'sayHi',{
    get: function(){
        return 'Hi ' + this.name;
    }
});
person.sayHi;
>>>"Hi Gerg"

⑦若只定义了属性的getter,则会忽略对该属性的赋值操作。

var person = {
    get name(){
        return 'Gerg';
    }
};
person.name = 'yyc';
person.name;
>>>"Gerg"

⑧getter和setter一起使用

var obj = {
    get a(){
        return this._a_;
    },
    set a(val){
        this._a_ = val * 2;
    }
};
obj.a = 2;
obj.a;
>>>4
7.存在性

①如何区分属性值为undefined,还是属性压根就不存在?

var person = {
    name: undefined
};
person.name;
>>>undefined

person.age;
>>>undefined

②方法一:in操作符

'name' in person;
>>>true

'age' in person;
>>>false

③方法二:hasOwnProperty()方法

person.hasOwnProperty('name');
>>>true

person.hasOwnProperty('age');
>>>false

④方法三:Object.keys()

Object.keys(person);
>>>["name"]

⑤方法四:Object.getOwnPropertyNames()

Object.getOwnPropertyNames(person);
>>>["name"]

⑥细节又来了!

var arr = [2,4,6];
4 in arr;
>>>false

for(var index in arr){
    console.log(index);
}
>>>
0
1
2

⑦枚举详解

var person = {};
Object.defineProperty(person,'name',{
    enumerable: true,
    value: 'Gerg'
});
Object.defineProperty(person,'age',{
    enumerable: false,
    value: 21
});
person.age;
>>>21

'age' in person;
>>>true

person.hasOwnProperty('age');
>>>true

for(var i in person){
    console.log(i,person[i]);
}
>>>name Gerg

在数组上应用for...in循环有时会产生出人意料的结果,因为这种枚举不仅会包含所有数值索引,还会包含所有可枚举属性。因此,最好只在对象上引用for...in循环,使用传统的for循环遍历数组。

方法二:propertyIsEnumerable()方法

var person = {};
Object.defineProperty(person,'name',{
    enumerable: true,
    value: 'Gerg'
});
Object.defineProperty(person,'age',{
    enumerable: false,
    value: 21
});
person.propertyIsEnumerable('name');
>>>true

person.propertyIsEnumerable('age');
>>>false

Object.keys(person);
>>>["name"]

Object.getOwnPropertyNames(person);
>>>(2) ["name", "age"]
ObjectKeys.png
上一篇下一篇

猜你喜欢

热点阅读