乐趣区

关于javascript:深入了解ObjectdefineProperty

JavaScript 中的对象
面向对象的语言有一个标记,那就是他们都有类的概念,而通过类能够创立任意多个具备雷同属性和办法的对象。ECMAScript 中没有类的概念,因而它的对象也与基于类的语言中的对象有所不同。

JavaScript 中的对象定义:无序属性的汇合,其属性能够蕴含根本值、对象或者函数。严格来讲,这就相当于说对象是一组没有特定程序的值。对象的每个属性或办法都有一个名字,而每个名字都映射到一个值。正因为这样,能够把对象设想成散列表,就是一个键值对,其中的值能够是任意的(数据,函数,对象,数组~~)

后面 balabala 一堆概念,真心感觉如果能够了解挺重要的

创建对象
能够应用构造函数,创立一个 Object 的实例,而后为其增加属性和办法

var person = new Object();
person.name = 'zhang';
person.age = 123;
person.job = "SoftWare Engineer";
person.sayName = function () {alert(this.name);
};
 
person.sayName();

这个例子创立了一个名为 person 的对象,并为其增加了三个属性(name、age、和 job)和一个办法(sayName),其中 sayName()办法用于显示 this.name 这个会被解析为 person.name 和值。

不过当初创立这种对象大都是通过字面量的形式来创立了

var person = {
    name: 'zhang',
    age: 18,
    job: 'SoftWare Engineer',
    sayName: function () {alert(this.name);
    }
};
 
person.sayName();

这种写法的后果和下面的那一种是一样的,这种写法如果你要拓展性能的话就不能再赋值了,也是用到了下面的写法

person.home = 'hebei';
person.showHome = function () {alert(this.home);
};

这个样子去拓展它。

属性类型
ES5 定义只有外部采纳的个性时,形容了属性的各种特色。定义这些个性时为了实现 JavaScript 引擎用的,因而在 JavaScript 中不能间接拜访它们。为了示意特色是外部值,该标准把它们放在了两对儿方括号中,例如[[Enumerable]]

后面例子中的 name,age,job,home,showHome 都是属性,引号前面的货色是值,说一下属性的一些货色。

ECMAScript 中有两种属性:数据属性和拜访器属性

数据属性
数据属性蕴含一个数据值的地位。在这个地位能够读取和写入值。数据属性有 4 个形容其行为的个性。

[[Configurable]] 示意是否通过 delete 删除属性从而从新定义属性,可能通过属性的个性,或者是否把属性批改为拜访器属性,像后面的例子中那样间接在对象上定义的属性,它们的这个个性默认值为 true。

[[Enumerable]] 示意是否通过 for-in 循环返回属性。像后面例子中那样间接在对象上定义的属性,他们的这个个性默认值为 true

[[Writable]] 示意可能批改属性的值。像后面例子中那样间接在对象上定义的属性,他们的这个个性默认值为 true

[[Value]] 蕴含这个属性的数据值。读取属性值的时候,从这个地位读;写入属性值的时候,把新值保留在这个地位。这个个性的默认值为 undefined

例子

var person = {name: 'zhang'};
 
for(var key in person){console.log(key);
}
// 他这个属性的 value 值就是他在下面所定义的那一些, 对这个值的任何批改都反映在这个地位
console.log(person.name);
 
person.name = 'goudan';
// 在这里也能够批改,因为 [[Writable]] 属性默认为 true
console.log(person.name);
 
// [[Configurable]] 这个货色为 true 示意也能够删除它
delete person.name;
console.log(person.name);   // undefined  因为删除之后这个属性就不存在了 [[Value]]个性的默认值为 undefined

重点来了

Object.defineProperty()
如果要批改 属性默认的个性 就必要要应用 ES5 的 Object。defineProperty()办法。这个办法接管三个参数。

属性所在的对象、属性的名字、一个描述符对象 (其中描述符对象的属性必须是: configurable、enumerable、writeable、和 value)设置其中一个或多个值,能够批改对应的个性值

上面去演示这些例子了,心愿你也能够试着去尝试一遍,我举得例子写法可能比拟啰嗦,心愿没有尝试过的能够试着练习一下

提醒:我前面 // 正文前面的货色 是输入打印的货色,如有谬误能够本人具体情况剖析一下

writable 的状况演示

var person = {
    name: 'zhang',
    age: 12
};
Object.defineProperty(person,"name",{writable: false});
// 读取 person.name
console.log(person.name);  // zhang
console.log(person.age); //12
// 批改 person.name
person.name = 'wang';
person.age = 14;
console.log(person.name);   // zhang  ?????? 咋没变
console.log(person.age);  // 14
// 起因:那个配置外面也说了的 [[Writable]] 默认是 true,然而改为 false 之后 就是这个属性不可写, 没有写进去的也能够看到是能够失常批改的

enumerable 的状况演示

  var person = {
    name: 'zhang',
    baba: '1',
    job: 'Software Engineer'
};
 
for(var key in person){console.log(key);  //   name, baba, job
}
//  三个属性都能够失常的打印进去 <br>// 配置一下 enumerable
Object.defineProperty(person,'baba',{enumerable: false});
 
for(var key1 in person){console.log(key1);   // name job
}
// ???   baba 去哪了?我通知你去哪了  因为配置了 enumerable: false
console.log(person.baba);  // 1  不会影响输入

Configurable 的演示

var person = {
    name: 'zhang',
    age: 12
};
 
Object.defineProperty(person,'name',{configurable: false});
 
console.log(person.name);   // zhang
console.log(person.age);    // 12
 
delete person.name;
console.log(person.name);  // zhang
// 方才删除不是 undefined 吗  configurable: false 就不容许删除了
// 如果申明是严格模式的话 那一句 delete person.name  还会报错
person.name =  'wang'
console.log(person.name); // wang

留神:这个配置有一点须要留神一下

var person = {name: 'zhang'};
Object.defineProperty(person,'name',{configurable: false});
// 如果当前你有需要你还想删除他的话 你会想到
Object.defineProperty(person,'name',{configurable: true});
// 控制台打印谬误音讯:Uncaught TypeError: Cannot redefine property: name at Function.defineProperty (<anonymous>)
// 这个货色一旦被配置为 false, 当前在也不能够把他变为 true 了 会报错

Value 的演示

var person = {name: 'zhang'};
console.log(person.name);  // zhang
Object.defineProperty(person,'name',{value: 'wang'});
console.log(person.name); // wang
// 你小子怎么改姓了
person.name = 'zhang';  // 给我改回来
console.log(person.name);  //zhang  又是我老张家的孩子了, 这个是能够批改回来的

当然了下面的属性只是独自演示的, 这四个属性能够联合起来应用,比方我要定义一个永远都无奈扭转它的值

var person = {name: 'zhang'};
console.log(person.name);  // zhang
Object.defineProperty(person,'name',{
    writable: false,
    value: 'wang'
});
 
console.log(person.name);  // wang
// 卧槽你个小兔崽子又改姓了 给我改回来
person.name = 'zhang';
console.log(person.name); // wang
// 你会发现 有些事件产生了就是产生了,永远也回不来了
person.name = 'zhang1';
console.log(person.name); // wang
  留神 在调用 Object.defineProperty()办法时,如果不指定,configurable、enumerable 和 writable 个性的默认值都是 true。

你给我演示这一堆货色到底有啥意思,无能啥。我想通知你少数状况下可能都没有必要利用 Object.defineProperty()办法提供的这些高级性能。不过,了解这些概念对了解 JavaScript 对象却十分有用

拜访器属性
拜访器属性不蕴含数据值;他们蕴含一对儿 getter 和 setter 函数(不过这两个函数都不是必须的) 在读取拜访器属性时,会调用 getter 函数,这个函数负责返回无效的值;在写入拜访器属性时,会调用 setter 函数并传入新值,这个函数负责决定如何解决数据。拜访器属性有如下 4 个个性

[[Configurable]] : 是否批改属性的个性,或者是否把属性批改为数据属性。像后面的例子中那样间接在对象上定义的属性,它们的这个个性默认值为 true

[[Enumerable]] : 是否通过 for-in 循环 * 和下面的一样

后面两个属性大同小异,简直是一样的,同样的拜访器属性不能间接定义,必须应用 Object.defineProperty() 来定义.

get 和 set 的演示

这个重要

var book = {
_year: 2018,
edition: 1
};
 
Object.defineProperty(book,'year',{get: function () {console.log(` 获取的时候触发函数 `);  // 每次获取的时候都会触发这个函数
    console.log(this === book); // true   这外面的 this 就是 book 的援用 也就是 book 对象
    return this._year;
},
set: function (newValue) {console.log('批改值的时候触发函数');
    // newValue ----- 这里的 newValue 就是你设置的批改后的值
    if(newValue> 2018){
        this._year = newValue;
        this.edition = newValue - 2018;
    }
}
});
 
console.log(book.year);
// 2018  他的值也就是咱们返回的值 book._year
// 如果 get 函数不写返回值就是 undefined
 
book.year = 2020; // 批改值的时候触发函数
console.log(book); // {_year: 2019, edition: 2};

vue 双向数据绑定就是这个,当咱们在批改数据的时候,会触发 set 那个函数,就在 set 函数中执行咱们的逻辑,去批改数据,当前我会写一个大略的双向数据绑定的那个代码,会收回来,网上也有好多相似的代码。

下面的代码创立了一个 book 对象,并给他定义两个默认的属性: _year 和 edition。

而拜访器属性 year 则蕴含一个 getter 函数和一个 setter 函数。getter 函数返回_year 的值,setter 函数批改了_year 和 edition。这是应用拜访器属性的常见形式,即设置一个属性的值会导致其余属性发生变化。

不肯定非要同时制订 getter 和 setter。只指定 getter 意味着属性是不能写,尝试写入属性会被疏忽。在严格模式下, 尝试写入至指定了 getter 函数的属性会抛出谬误。相似的,只指定 setter 函数的属性也不能读,否则在非严格模式下回返回 undefined,而在严格模式下会抛出谬误。

同时定义多个属性的写法因为未对象定义多个属性的可能性很大,ES5 又定义了一个 Object.defineProperties()办法利用这个办法能够通过描述符一次定义多个属性。

这个办法接管两个对象参数:第一个对象是哟啊增加和批改其属性的对象,第二个对象的属性与第一个对象中要增加或批改的属性一一对应。

同时定义多个属性的写法

var book = {};
 
Object.defineProperties(book,{
    _year: {value: 2018},
    edition: {value: 1},
    year: {get: function () {return this._year;},
        set: function (newValue) {console.log('批改函数触发触发了吗');
            if(newValue>2018){
                this._year = newValue;
                this.edition += newValue - 2018;
            }
        }
    }
});
 
console.log(book.year);
 
book.year = 2020;
console.log(book);  //{_year: 2018, edition: 1}
//????我后面不是触发了批改的函数了吗为什么没有改呢 上一个离开写的都批改胜利了
  

别着急,上面来看一个新的 API

Object.getOwnPropertyDescriptor()
读取属性的个性

应用 Es5 的 Object.getOwnPropertyDescription() 办法,能够获得给定属性描述符。这个办法接管两个参数: 属性所在的对象和要读取其描述符的属性名称。返回值是一个对象,如果是拜访器属性,这个对象的属性有 configurable enumerable writable 和 value

var descriptor1 = Object.getOwnPropertyDescriptor(book,"_year");
var descriptor2 = Object.getOwnPropertyDescriptor(book,"edition");
console.log(descriptor1);
/*
{configurable: false,
enumerable: false,
value: 2018,
writable: false}
*/
console.log(descriptor2);
/*
    {
    configurable: false,
    enumerable: false,
    value: 1,
    writable: false}
 */
 */

后面离开写能够胜利的起因能够用这个货色去获取一下,能够看到他们是 true 的,这个是 false,所以
尽管你批改了,然而他是不失效的
在 JavaScript 中,能够针对任何对象 —— 包含 DOM 和 BOM 对象,应用Object.getOwnPropertyDescriptor() 办法。

退出移动版