《JavaScript高级程序设计》-- 初识对象

ECMAScript 中的对象可以理解成一个散列表,其中的内容就是一组名/值对,值可以是数据或者函数

var person = {
    
    
	name:'petter',
	age:26,
	job: 'software engineer',
	sayName(){
    
    
	  console.log(this.name);
    }
}
console.log(person.job) //software engineer
person.sayName(); //petter

创建了一个名为person的对象,包含三个属性name,age,job和一个方法sayName()

属性的类型

ECMA-262 使用一些内部特性来描述属性的特征。为了将某个特性标识为内部特征,规范会用两个中括号把特性的名称括起来。例如[[Configurable]]

属性分为数据属性和访问器属性

数据属性

数据属性包含一个保存数据值的位置。值会从这个位置读取,也会写入到这个位置。数据属性有四个特性描述他们的行为。

  • [[Configurable]]: 表示属性是否可以通过delete删除并重新定义,是否可以修改它的特性,以及是否可以把它改为访问器属性。默认该特性为true
  • [[Enumerable]]:表示属性是否可以通过for-in循环返回。默认该特性为true
  • [[Writable]]:表示属性的值是否可以被修改。默认该特性为true
  • [[Value]]:包含属性实际的值。就是前面提到那个读取和写入属性值的位置。默认为undefined
let person = {
    
    
    name: 'petter'
}

属性name添加到person对象后,[[Configuable]]、[[Enumerable]]、[[Writable]]都会设置为true,而[[Value]]特性会被设置为指定的值‘petter’,后面对这个值的任何修改都会保存在这个位置。

Object.defineProperty()方法可以修改属性的默认特性,该方法接收三个参数:要给其添加属性的对象、属性的名称和一个描述符对象。描述符对象属性可以包含:Configuable、Enumerable、Writabe、Value,跟相关特性的名称一一对应。

例如:

var person = {
    
    
    name: 'petter'
}
//修改name属性值为herry,且该属性为只读不可以再修改
Object.defineProperty(person,'name',{
    
    writable:false,value:'herry'});
console.log(person.name) //herry
//属性值重新赋值失败
person.name='jon';
console.log(person.name) //herry

包含一个属性为name的对象person,给该属性name重新赋值value为herry,并且修改writable为false表示该属性值不可以再修改,非严格模式下修改属性值被忽略,严格模式修改属性值会报错。

var person = {
    
    }
Object.defineProperty(person,'name',{
    
    configurable:false,value:'pettry'});
console.log(person.name) //pettry
//修改失败
person.name="herry"
console.log(person.name) //pettry
//删除失败,严格模式会报错,非严格模式会忽略
delete person.name;
console.log(person.name) //pettry

// 配置完configurable为false之后,再配置为true会报错
Object.defineProperty(person,'name',{
    
    configurable:true});
// Uncaught TypeError: Cannot redefine property: name
// at Function.defineProperty (<anonymous>)

configuable设置为false意味着这个属性不能从对象上删除和修改。此外如果一个属性被定义为不可配置之后就不能再变回可配置的了。

访问器属性

访问器属性不包含数据值。他们包含一个获取(getter)函数和一个设置(setter)函数。在读取访问器属性时,会调用获取函数,在写入访问器属性时,会调用设置函数并传入新值。访问器属性有四个特性描述他的行为。

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

访问器属性时不能直接定义的,必须通过Object.defineProperty()。

//定义数据属性year_和edition
let book = {
    
    
    year_: 2017,
    edition: 1
}
//定义访问器属性year
Object.defineProperty(book,'year',{
    
    
	get(){
    
    
		return this.year_;
	},
	set(newValue) {
    
    
		if(newValue>2017) {
    
    
			this.year_ = newValue;
			this.edition += newValue-2017;
		}
	}
})
book.year=2019;
console.log(book); //{year_: 2019, edition: 3}

对象book有两个默认属性year_、edition,另一个属性year被定义为一个访问器属性,其中设置函数对赋给year的属性值做处理,year的值会影响默认属性year_和edition的值。

如果定义的访问器属性只有一个读取函数,则表示这个属性是只读的,设置该属性会出错,同样如果只有一个设置函数表示这个属性只能设置不可以获取。

定义多个属性

Object.defineProperties()方法可以通过多个描述符一次性定义多个属性。它接收两个参数:要为之添加或修改属性的对象和另一个描述符对象,描述符对象属性与要添加或修改的属性一一对应。

let book = {
    
    };
Object.defineProperties(book,{
    
    
    year_: {
    
    
        value:2017,
        //writable:true
    },
    edition: {
    
    
      value:1  
    },
    year: {
    
    
        get(){
    
    
		    return this.year_;
        },
        set(newValue) {
    
    
            if(newValue>2017) {
    
    
                this.year_ = newValue;
                this.edition += newValue-2017;
            }
        }
    }	
})
console.log(book.year)//2017
//修改失败,默认writable为false,想要修改在year_属性值可添加'writable : true'
book.year_ = 2018
console.log(book) //{year_: 2017, edition: 1}

这种方法定义的对象,数据属性的configurable、enumerable和writable特征值都是false。

读取属性的特性

使用Object.getOwnPropertyDescriptor()方法可以获得指定属性的属性描述符。接收两个参数:属性所在的对象和要取得其描述符的属性名。返回值是一个对象,对于数据属性包含configurable、enumerable、writable、value属性值,对于访问器属性包含configurable、enumerable、get、set属性。

let book = {
    
    };
Object.defineProperties(book,{
    
    
    year_: {
    
    
        value:2017
    },
    edition: {
    
    
      value:1  
    },
    year: {
    
    
        get(){
    
    
		    return this.year_;
        },
        set(newValue) {
    
    
            if(newValue>2017) {
    
    
                this.year_ = newValue;
                this.edition += newValue-2017;
            }
        }
    }	
})

//获取访问器属性的特性
var descriptor = Object.getOwnPropertyDescriptor(book,'year');
console.log(descriptor) //{enumerable: false, configurable: false, get: ƒ, set: ƒ}
//获取数据属性的特性
var descriptor = Object.getOwnPropertyDescriptor(book,'year_')
console.log(descriptor) //{value: 2017, writable: false, enumerable: false, configurable: false}

ECMAScript2017新增了Object.getOwnPropertyDescriptors()静态方法。这个方法实际上会在每个自有属性上调用Object.getOwnPropertyDescriptor()并在一个新对象中返回他们。

let book = {
    
    };
Object.defineProperties(book,{
    
    
    year_: {
    
    
        value:2017
    },
    edition: {
    
    
      value:1  
    },
    year: {
    
    
        get(){
    
    
		    return this.year_;
        },
        set(newValue) {
    
    
            if(newValue>2017) {
    
    
                this.year_ = newValue;
                this.edition += newValue-2017;
            }
        }
    }	
})

var descriptors = Object.getOwnPropertyDescriptors(book);
console.log(descriptors);
// {
    
    
//    edition: {value: 1, writable: false, enumerable: false, configurable: false},
//    year: {enumerable: false, configurable: false, get: ƒ, set: ƒ},
//    year_: {value: 2017, writable: false, enumerable: false, configurable: false}
// }

合并对象

ES6新增方法Object.assign()方法,可以把源对象所有的本地属性一起复制到目标对象上。该方法接收一个目标对象和一个或多个源对象作为参数,然后将源对象中可枚举和自由属性复制到目标对象。以字符串和符号为键的属性会被复制,对于每一个符合条件的属性,该方法会使用源对象上的[[Get]]取得属性的值,然后使用目标对象上[[Set]]设置属性的值。

var des = {
    
    a:'foo'};
//多个源对象有相同的属性,则使用最后一个复制的值。
var result = Object.assign(des,{
    
    a:'bar'},{
    
    b:'dog'},{
    
    c:'car'},{
    
    b:'tar'})
console.log(result) //{a: "bar", b: "tar", c: "car"}

Object.assign()没办法回滚已经完成的修改

var dest = {
    
    };
var src = {
    
    
	a: 'foo',	
	get b() {
    
    
	  throw new Error();
	},	
	c: 'bar'
}

try {
    
    	
    Object.assign(dest,src);
}catch(e) {
    
    }

console.log(dest) //{a: "foo"} 

因为Object.assign操作不可以回滚所以在抛出错误之前,已完成的修改会继续存在。

增强的对象语法

属性值的简写

当属性名和变量名相同时

let name = 'herry';
// 正常写法
// let person = { name:name	}
let person = {
    
     name };
console.log(person) //{name: "herry"}

可计算的属性

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

const nameKey = 'name';
const ageKey = 'age';
let person = {
    
    };
person[nameKey] = 'herry';
person[ageKey] = 26;
console.log(person); //{name: "herry", age: 26}

定义了两个变量nameKey和ageKey,然后把这两个变量作为person对象的属性。

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

可计算属性本身可以是复杂的表达式,在实例化时再求值。

简写方法名

//正常的写法
var person = {
    
        
    sayName: function(name) {
    
    		
      console.log('my name is '+name);    
    }
}
person.sayName('herry'); //my name is herry
//简写方法名
var person = {
    
        
    sayName(name) {
    
            
      console.log('my name is '+name);    
    }
}
person.sayName('petter'); //my name is petter
//简写方法名与可计算属性键相互兼容
const methodKey = 'sayName';
var person = {
    
    	
    [methodKey](name) {
    
            
      console.log('my name is '+name);    
    }}
person.sayName('matt'); //my name is matt

对象解构

es6新增了对象解构语法。对象解构就是使用与对象匹配的结构来实现对象属性赋值

var person = {
    
    
    name: 'herry',
    age: 26
}
//不使用对象解构
var personName = person.name;
var personAge = person.age;

//personName herry,personAge 26
console.log(`personName ${
      
      personName},personAge ${
      
      personAge}`); 


//使用对象解构
var {
    
    name: personName,age:personAge} = person;
//personName herry,personAge 26
console.log(`personName ${
      
      personName},personAge ${
      
      personAge}`);

//如果变量名称和属性名相同
var {
    
    name,age} = person;
//name herry,age 26
console.log(`name ${
      
      name},age ${
      
      age}`);

//解构赋值的同时定义默认值
var {
    
    name,age,job = 'software enginer'} = person;
//person里面没有job属性,如果不定义默认值,job默认为undefined
console.log('personJob ',job); //personJob  software enginer


var personName,personAge;
//如果赋值的变量在解构之前就已经定义了,赋值表达式必须在一对括号中
//Uncaught SyntaxError: Unexpected token ':'
//{name:personName,age:personAge} = person;
({
    
    name:personName,age:personAge} = person);
//personName herry,personAge 26
console.log(`personName ${
      
      personName},personAge ${
      
      personAge}`);

嵌套解构

解构对于引用嵌套的属性或赋值目标没有限制

var person = {
    
    
    name:'petter',
    age:26,
    job:{
    
    
        title: 'software engineer'
    }
}
var {
    
    name,job: {
    
    title} } = person;
console.log('title ',title); //title  software engineer


参数上下文匹配

函数参数列表中也可以进行解构赋值。

var person ={
    
    
    name:'herry',
    age:26,
}
function printPerson ({
    
    name,age}) {
    
    
    console.log(name,age);
}

function printPerson1({
    
    name:personName,age:personAge}) {
    
    
    console.log(personName,personAge);
}
printPerson(person); //herry 26
printPerson1(person); //herry 26

猜你喜欢

转载自blog.csdn.net/weixin_43398820/article/details/118458487