ES6-对象拓展

时间:2021-1-18 作者:admin

对象拓展

ES6除了对对象的属性和方法进行简化之外,值得注意的是对象的属性名都会被处理成字符串

例1:

var arr = [5,6,8,88];
console.log(arr['1']); //6

数组是特殊的对象,当你用arr[1]的时候,其实是会经过隐式转换成arr[‘1’]的,所以可以打印出值

例2:

let a = 'hello';
let b = 'world';
let obj = {
    [a + b] : true,
    ['hello' + 'world']: undefined
}

最后的值是undefined,同样的属性,js不会检查属性值,会直接进行覆盖

例3:

var myObj = {};
myObj[true] = 'foo';
myObj[3] = 'bar';
myObj[myObj] = 'baz';
console.log(myObj); //{'true':'foo','3':'bar','[object Object]': "baz"}

直接变成字符串,属性处理成字符串都要经过隐式转换(toString)

true和3经过原型上的toString方法都会变成对应的字符串,myObj经过toString方法会变成[object Object],所以直接通过[object Object]可以访问到baz

console.log(myObj["[object Object]"]); //baz

例4:

const a = {a: 1};
const b = {b: 2};
const obj = {
    [a]: 'valueA',
    [b]: 'valueB'
}
console.log(obj); //{[object Object]: 'valueB'},都是引用值,会有覆盖值的问题

引用变量一定要加括号,不然就变成{a: “valueA”, b: “valueB”},直接转化不引用了。


获取对象方法的名称

const person = {
    sayName(){
        console.log('hello');
    }
}
console.log(person.sayName.name); //sayName

属性描述符 getOwnPropertyDescriptor

ES5之前没有检测属性特性(是否只读,可遍历等)的方法,ES6提供了属性描述符

let obj = {a: 2};
console.log(Object.getOwnPropertyDescriptor(obj, 'a'));

传入的属性要是字符串

configurable: true //可配置的
enumerable: true   //可枚举的
value: 2           
writable: true     //可写

defineProperty修改和添加一个新的属性

Object.defineProperty(obj, prop, descriptor),descriptor 描述项集合、配置集合

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

应当直接在 Object 构造器对象上调用此方法,而不是在任意一个 Object 类型的实例上调用。因为这个方法是在Object上的,而不是Object.prototype上的,实例不能继承。

let obj = {};
Object.defineProperty(obj, 'a', {
    value: 2,
    enumerable: true,
    configurable: true, //false的话就不能再用defineProperty配置了
    writable: false //不可写但是可以删除
})
obj.a = 3;//静默失败,因为不可写,这条语句执行失败,但也不报错;但是在严格模式下会报错
console.log(obj); //{a: 2}
console.log(Object.getOwnPropertyDescriptor(obj, 'a'));
delete obj.a;
console.log(obj); //{}

writable设置false仅仅是不可写,不可删要是configurable为false的情况下才可以

另一种写法:

function defineProperty () {
    var _obj = {};
    Object.defineProperty(_obj, 'a', {
        value: 1
    });
    return _obj;
}
var obj = defineProperty();
console.log(obj);

还可以设置多个属性:defineProperties

function defineProperty () {
    var _obj = {};
    Object.defineProperties(_obj, {
        a: {
            value: 1,
            writable: true,
            enumerable: true, //可否枚举
            configurable: true //可否配置、操作(删除)
        },
        b: {
            value: 2
        }
    });
    return _obj;
}
var obj = defineProperty();
obj.a = 5;
console.log(obj.a);//值在writable:true之前是1,不可修改,加了writable之后改为5

用Object.defineProperty()定义的属性值默认不可修改,不可删除,不可枚举,除非用writable / enumerable修改

configure -> conf(一般用来设置文件夹)/ config(一般用来设置文件名)

getter / setter 取值和赋值

每一个属性定义的时候,都会产生getter和setter的机制

在赋值和取值的时候可以设置一些额外的操作

get操作

let obj = {a: 1};
obj.a;  //属性获取,会有[[Get]]的默认操作:查找当前属性,如果没有,查找原型

put操作

obj.a = 3; //赋值操作[[Put]]
  1. getter 还是 setter
  2. writable:false,不让你改
  3. 赋值

getter和setter覆盖了原本的[[Get]]和[[Set]]

getter:取值函数

var obj = {
    log: ['example', 'test'],
    get lastest(){  //get方式的伪属性
        if(this.log.length === 0){
            return undefined;
        }
        return this.log[this.log.length - 1];
    }
}
console.log(obj.lastest); //访问属性,实际上调用的是get方法

通过defineProperty定义getter,value和writable不能用

var obj = {
    get a(){
        return 2;
    }
}
Object.defineProperty(obj, 'b', {
    get: function)() {
        return this.a * 2;
    }
    enumerable: true,
    value: 6 //报错,和get操作重复
    writable: true //不管是true还是false都报错,不能定义
})

setter:设值函数

var language = {
    set current(name){  //一定要有参数
        this.log.push(name)
    },
    log: []
}
language.current = 'en';

get和set一般情况下都是成对存在的,get和set的函数名称不能直接获取到,要通过getOwnPropertyDescriptor获取到函数才行

var obj = {
    get foo(){
        return this._a;
    },
    set foo(val){
        this._a = val * 2;
    }
}
obj.foo = 3; //set操作
console.log(obj.foo); //6

console.log(obj.foo.name); //undefined,不能取到name
var descriptor = Object.getOwnPropertyDescriptor(obj, 'foo');
console.log(descriptor.get.name);
console.log(descriptor.set.name);

应用场景:

<p>0</p>
function defineProperty () {
    var _obj = {};
    var a = 1;

    Object.defineProperties(_obj, {
        a: {
            get () {

            },
            set (newVal) {
                a = newVal;
                var oP = document.getElementsByTagName('p')[0];
                oP.innerHTML = a;
            }
        },
        b: {
            value: 2
        }
    });

    return _obj;
}

var obj = defineProperty();
obj.a = 5;

这样p标签的值就可以改了

function defineProperty () {
    var _obj = {};
    var a = 1;

    Object.defineProperties(_obj, {
        a: {
            get () {
                return 'a\'s value is '+ a + '.';
            },
            set (newVal) {
                console.log('The value has been designed a new value ' + a)
            }
        },
        b: {
            value: 2
        }
    });
    return _obj;
}

var obj = defineProperty();
obj.a = 5; //set,5就是传入的newVal值,但是set程序里并没有改变a的值,所以a不变
console.log(obj.a); //get

当我们设置值和取值的时候,得到的结果是

The value has been designed a new value 1
a's value is 1.

数据劫持,指的是在访问或者修改对象的某个属性时,通过一段代码拦截这个行为,进行额外的操作或者修改返回结果。

也就是在取值的时候get程序设置了别的程序,让你取不到真的值,阻拦获取和输入。

在全局设置数据值,用get和set方法扩展逻辑

value默认值 / writable 和 set、get不能共存,同时出现会报错!


操作数组

function DataArr () {
    var _val = null,
        _arr = [];

    Object.defineProperty(this, 'val', {
        get: function () {
            return _val;
        },
        set: function(newVal) {
            _val = newVal;
            _arr.push({val: _val});
            console.log('A new value ' + _val + ' has been pushed to _arr');
        }
    });

    this.getArr = function(){
        return _arr;
    }
}

var dataArr = new DataArr();
dataArr.val = 123;
dataArr.val = 234;
console.log(dataArr.getArr());

对象密封

对象常量:不可删除,不可修改

1. Object.defineProperty配置属性描述符

configurable: false
enumerable: true 
value: 2           
writable: false  

正常添加属性obj.b=3,默认情况下除了value以外的三个值都是true

通过Object.defineProperty添加属性,默认情况下这三个值都是false

2. preventExtensions 不可拓展(添加新的属性或方法)

preventExtensions不能改变属性描述符

isExtensible 判断对象是否可以拓展

var obj = {a: 2};
console.log(Object.preventExtensions(obj)); //返回该对象
obj.b = 3; //静默失败,严格模式下会报错
console.log(Object.isExtensible(obj)); //false,不可拓展

仍然可以删除,可修改

3. Object.seal(obj)

seal将configurable变成false

isSealed 是否密封

var obj = {a: 2};
console.log(Object.seal(obj));

但是writable还是true的,也就是说可修改

4. Object.freeze(obj)

freeze会把configurable和writable都会是false,这只是浅拷贝,要想深度冻结,需要循环冻结对象上的方法

isFrozen 是否被冻住

function myFreeze(obj) {
    Object.freeze(obj);
    for(var key in obj) {
        if (typeof(obj[key]) === 'object' && obj[key] !== null) {
            myFreeze(obj[key]);
        }
    }
}

一般用freeze


对象原型上的其他方法

Object.is()判断是否全等

‘==’会隐式转换

‘===’严格相对运算符,会调用底层的sameValue算法

ES5要判断两个值是否相等,需要用运算符判断

console.log(NaN === NaN); //false
console.log(+0 === -0); //true

ES6可以调用is方法判断,用的是‘===’

和ES5不同的结果只有这两种

console.log(Object.is(NaN, NaN)); //true
console.log(+0 === -0); //false

Object.keys() 获取自身可枚举的键名

Object.value() 获取自身可枚举的键值

Object.entriese() 获取自身可枚举的键名和键值

不含原型上的属性

coonst foo = {a: 1};
Object.defineProperties(foo, {
    d: {
        value: 4,
        enumerable: true
    },
    f: {
        value: 5,
        enumerable: false
    }
})
console.log(Object.keys(foo)); //["a", "d"],f不可枚举
console.log(Object.value(foo)); //[1,4,5]
console.log(Object.entries(foo)); //[["a",1],["d",4]]

传入的参数不是对象的话,会隐式转换,进行包装类

let obj = 1;
console.log(Object.keys(obj)); //[]
let obj = 'abc';
console.log(Object.keys(obj)); //["1","2","3"]

Object.assign(tar, …sourses)合并对象

对象拷贝(浅拷贝)

let obj = {a: {b: 1}};
let tar = {};
let copy = Object.assign(tar, obj); //返回值就是第一个参数

console.log(copy === tar); //true
console.log(copy === obj); //false

obj.a.b = 2;
console.log(obj.a.b); //2

同名属性的替换:

const tar = {a: 1, b: 1};
const tar1 = {b: 2, c: 2};
const tar2 = {c: 3};
Object.assign(tar, tar1, tar2);
console.log(tar); //{a: 1, b: 2, c: 3}后面的覆盖前面的

数组替换:两个属性下标相同的部分会被替换

Object.assign([1,2,3],[4,5]);//[4,5,3]

传值不是对象的情况:

Object.assign(undefined, {a: 1});//报错
var test = Object.assign(1, {a: 1};//用包装类的方式,转换为对象
console.log(test); //Number{1, a: 1}

第一个参数至少要是一个对象,undefined没有对应的包装类,无法进行合并

Object.assign({a: 1}, undefined);//{a: 1}
Object.assign({a: 1}, 1);//{a: 1}
Object.assign({a: 1}, true);//{a: 1}
Object.assign({a: 1}, '123');//{0: "1", 1: "2", 2: "3", a: 1}
Object.assign({}, '123', true, 10);//{0: "1", 1: "2", 2: "3"}

如果第二个参数无法转换为对象,就不进行任何处理,直接返回第一个参数的对象

如果第二个参数是对象,还要注意是否具有可枚举性,不具有枚举性的也会忽略

let obj = {a: 1};
Object.defineProperty(obj, 'b', {})

Object.create(proto, [propertiesObject])

第一个参数指定原型,第二个参数配置属性和对应的描述符

var obj = Object.create({foo: 1}, {
    bar: {
        value: 2
    },
    baz: {
        value: 3,
        enumerable: true
    }
})
console.log(obj); //{baz: 3, bar: 2}

let copy = Object.assign({}, obj);
console.log(copy); //{baz: 3},并且原型上的foo没有拷贝上去

继承属性和不可枚举属性,不能拷贝

Symbol也可以用assign进行拷贝,symbol()可以生成完全不一样的,永远不会重复的,类似于字符串的原始类型

var test = Object.assign({a: b}, {[Symbol('c')] : 'd'});
console.log(test)//{a: "b", Symbol(c): "d"}

在原型上扩充:

function Person(){}
var age = 1;
Object.assign(Person.prototype, {
    eat(){},
    age,
})

覆盖的正确做法:

const DEFAULT = {
    url: {
        host: 'www.baidu.com',
        port: 7070
    }
}
function test(option){
    option = Object.assign({}, DEFAULT, option);
}
test({url: {port: 8080}})

用户如果没有给值,就用DEFAULT,如果用户配置了,就会替代DEFAULT


拷贝取值函数:

const source = {
    get foo(){
        return 1;
    }
}
const target = {};
Object.assign(target, source); //{foo: 1}

拷贝的不是函数体本身,直接拷贝具体的值

所以用assign不能达到我们的目的,要用getOwnPropertyDescriptor

Object.defineProperties(tar, Object.getOwnPropertyDescriptor(source));
console.log(Object.getOwnPropertyDescriptor(tar, 'foo'));

这个方法完美解决了这个问题,并且连getter和setter也能拷贝


并且getOwnPropertyDescriptors 很容易浅拷贝

利用getPropertyOf获取原型,getOwnPropertyDescriptors获取属性,然后创建对象即可

const clone = Object.create(Object.getPropertyOf(obj), Object.getOwnPropertyDescriptors(obj));

部署对象的方式

const obj = {a: 1};
const obj = Object.create(port);

没有get和set的情况下是可以用assign的

const obj = Object.assign(Object.create(port), {
    foo: 123
})
const obj = Object.create(port, Object.getOwnPropertyDescriptors({
    foo: 123
}));
声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。