Skip to content

Latest commit

 

History

History
257 lines (204 loc) · 7.59 KB

对象.md

File metadata and controls

257 lines (204 loc) · 7.59 KB

对象

理解对象

创建自定义对象的通常方式是创建Object的一个新实例,然后在给它添加属性和方法。

let person = new Object();
person.name = 'Ljfy';
person.age = '32';
person.job = 'Software';
person.sayName = function(){
    console.log(this.name);
}
//现在写法
let person = {
    name:"Ljfy",
    age = 32,
    job:"Software",
    sayName(){
        console.log(this.name);
    }
}

属性的类型

ECMA-26使用了一些内部特性来描述属性的特征,这些特性由JavaScript实现引擎定义的,不能直接访问这些特性,规范会用中括号把特性括起来[[Enumerable]]

属性分两种:数据属性和访问器属性

  • 数据属性:数据属性包含了一个保存数据值的位置。值会从这个位子读取,也会写入这个位置,数据属性的四个特性:
    • [[Configurable]]:表示属性可以通过delete删除并重新定义,是否可修改它的特性,以及是否可以把它改为访问器属性。默认为true
    • [[Enumerable]]:表示属性是否可以通过for-in循环返回,默认情况下为true
    • [[Writable]]:表示属性的值是否可以被修改.默认为true
    • [[Value]]:包含实际属性值,读取和写入属性的位置,默认值为undefined,Value特性会被设为指定的值。
let person = {
    name :"child"
}

此时创建了一个名为name的属性,并给他赋予了一个值child,这意味着[[Value]]特性会被设置为child,之后对这个值的任何修改都会保存这个位置。

要修改属性的默认特性,必须使用Object.defineProperty()方法这个方法接收三个参数:

要给其属性的对象,属性的名称和一个描述对象,最后一个参数,即描述对象上的属性可以包含ConfigurableEnumerableWritableValue,跟相关的特性一一对应,根据要修改的特性,修改一个或多个值比如:

let person = {};
Object.defineProperty(person,"name",{
    writable:false,
    value:"aa"
});
console.log(person.name);
person.name = "bb";
console.log(person.name);

这个例子创建了一个名为name的属性并给他赋予了一个只读的值aa这个属性值就不能修改了,在非严格模式下给这个属性重新复制会被忽略。在严格模式下,尝试修改只读属性的值会抛出错误。

比如:

let person = {};
Object.defindProperty(person,'name',{
    configurable:false,
    value:"aa"
});
console.log(person.name);//aa

上面这个例子把configurable设置为false,意味着这个属性不能从对象上删除。非严格模式下对这个属性调用delete没有效果,严格模式会报错

访问器属性

访问器属性不包含数据值,相反.它们包含一个获取(getter)函数和一个设置(setter)函数,这个函数不是必须的,在读取访问器属性的时候,会调用获取函数,这个函数的责任就是返回一个有效值。在写入访问器属性时,会调用设置函数并传入新的值,这个函数必须决定对数据做出什么修改。

访问器四个特性:

  • [[Configurable]]:表示属性可以通过delete删除并重新定义,是否可修改它的特性,以及是否可以把它改为访问器属性。默认为true
  • [[Enumerable]]:表示属性是否可以通过for-in循环返回,默认情况下为true
  • [[get]]:获取函数,在读取属性时调用,默认值为undefined
  • [[set]]:设置函数时,在写入属性时调用。默认值为undefined

访问器属性不能直接定义,必须使用Object.defineProperty()

let book = {
    yaer_:2020,
    edition:1
};
Object.defineProperty(book,"year",{
    get(){
        return this.year_;
    },
    set(newValue){
       if(newVlue > 2020){
           this.year_ = newValue;
           this.edition += newValue - 2020;
    }
  }      
})
book.year = 2021;
console.log(book.edition);//2

定义一个对象,包含伪私有成员year_和公共成员,对象book有两个默认属性:yearedition

year中下划线常用来表示该属性不希望在对象方法的外部别访问。另一个属性year被定义为一个访问器属性,获取函数简单的返回year_的值,而设置函数会做一些计算以决定正确的版本,因此,把year属性修改2021回导致year_变成2021,edition变成2,这是典型的应用场景

对象标识及相等判定

在es6之前,有些特殊情况即使是 ===也是无能为力,无可奈何的。

console.log(true === 1); //false
console.log({} === {}); //false
console.log("2" === 2);//false

//下列这些情况在不同JavaScript引擎中表现不同,但仍被认为相等
console.log(+0 === -0);//true
console.log(+0 === 0);//true
console.log(-0 === 0);//true

//要确定NaN的相等性,必须使用极为讨厌的isNaN()
console.log(NaN === NaN);//false
console.log(isNaN(NaN));//true

为此,ES6规范新增了Object.is()===很像 。这个方法接收两个参数。例如:

console.log(Object.is(true,1));//false
console.log(Object.js({},{}));//false
console.log(Object.is("2",2));//false
//正确的0、 -0、+0相等/不等判定
console.log(Object.is(+0,-0));//false
console.log(Object.is(+0,0));//true
console.log(Object.is(-0,0));//false
//正确的NaN相等判定
console.log(Object.is(NaN,NaN));//true

属性值简写

let name = 'Ljfy';
let person ={
    name
};
console.log(person);//{name:"Ljfy"}

代码压缩会在不同作用域间保留属性名。

function makePerson(name){
    return{
        name
    };
}
let person = makePerson('Ljfy');
console.log(person.name);//Ljfy

即使参数标识符只限定于函数作用域,编译器也会保留初始的name标识符。如果使用Google Closure编译器压缩,那么函数参数会被缩短,属性名不变。

function makePerson(a){
    return{
        name:a
    }
}
let person = makePerson('Ljfy');
console.log(person.name);//Ljfy

可计算属性

如果想使用变量的值作为属性,那么必须先声明对象,然后使用中括号来添加属性。

const nameKey = 'name';
const ageKey = 'age';
const jobKey = 'job';

let person = {};
person[nameKey] = 'Ljty';
person[ageKey] = 22;
personP[jobKey] = 'Soft'
console.log(person);//{name:'Ljty',age:22,j:'Soft'}

可以是更复杂的表达式

const nameKey = 'name';
const ageKey = 'age';
const jobKey = 'job';
let uniqueToken = 0;
function getUniqueKey(key){
    return `${key}_${uniqueToken++}`;
}
let person = {
    [getUniqueKey[nameKey]]:"Ljfy",
    [getUniqueKey[ageKey]]:22,
    [getUniqueKey[jobKey]]:"Soft"
}
console.log(person);//{name_0: "Ljfy", age_1: 22, job_2: "Soft"}

简写方法名

let person = {
    sayName(name){
        console.log('my name os ${name}');
    }
}
person.sayName('Matt');

浅克隆

浅克隆之所以被称为浅克隆,是因为对象只会被克隆最外部的一层,至于更深层的对象,则依然通过引用指向同一块堆内存

const  olobj={
            a:1,
            b:['e','f','g'],
            c:{h:{i:2}}
        };
        function shallowClone(o){
            const obj = {};
            for(let i in o){
                obj[i] = o[i];
            }
            return obj;
        }
        const  newObj = shallowClone(olobj);
        console.log(newObj.c.h,olobj.c.h);
        console.log(olobj.c.h === newObj.c.h);

深克隆

JSON.parse()方法

const obj = JSON.parse(JSON.stringify(oldObj))

缺点:

  • 无法实现对函数、RegExp等特殊对象的克隆
  • 会抛弃对象的constructor,所有的构造函数会指向Object
  • 对象循环有引用会报错