访问器
在ECMAScript5中,提供了Object.defineProperty的方法,我们可以通过该方法来控制属性的更多权限。
属性类型
我们先看一段定义属性的代码:
1 var person = { 2 name:"Li Lei" 3 }; 4 console.log(person.name);
我们为person定义了一个名为name的属性,这是最简单的设定,那么当我希望这个属性是一个只读属性,或者该属性不会在for in循环中被遍历该怎么办呢,这就要用到Object.defineProperty方法了。
我们先认识一下该方法可以控制的属性类型有哪些:
- configurable:该属性是否可以通过delete关键字删除,默认为false。
- enumerable:该属性是否在for in循环中出现,默认值为false。
- writable:该属性是否可写,默认值为false。
- value:属性的默认值,默认值为undefined。
或者
- configurable:同上
- enumerable:同上
- set:写入属性时调用的函数,只设定该方法时为只写。
- get:读取属性时调用的函数,只设定该方法时为只读。
需要注意的是,直接定义在对象上的属性,configurable、enumerable和writable都是true。
这里的Writable、Value和Set、Get是不应该同时出现的,所以分为了两组。
示例
我们先看第一种搭配:
1 var person = {}; 2 3 // 默认是只读、不可删除且不能被 for in 遍历的属性 4 Object.defineProperty(person, "color", { 5 value: "yellow" 6 }); 7 8 person.color = "black"; 9 console.log(person.color); // yellow 10 delete person.color; 11 console.log(person.color); // yellow 12 13 // 定义为可读写的属性 14 Object.defineProperty(person, "sex", { 15 writable: true, 16 value: "male" 17 }); 18 19 person.sex = "female"; 20 console.log(person.sex); // female 21 22 // 定义为可删除属性 23 Object.defineProperty(person, "name", { 24 configurable: true, 25 value: "Li Lei" 26 }); 27 28 delete person.name; 29 console.log(person.name); // undefined 30 31 // 定义为会被 for in 枚举属性 32 Object.defineProperty(person, "age", { 33 enumerable: true, 34 value: 28 35 }); 36 37 for(var k in person) { 38 console.log(k); // age 39 }
我们再看下一种:
1 var person = { 2 _birthday:0, 3 _age:0 4 }; 5 6 Object.defineProperty(person, "birthday", { 7 set:function(value) { 8 this._birthday = value; 9 this._age = 2017 - this._birthday; 10 }, 11 get:function() { 12 return this._birthday; 13 } 14 }); 15 16 Object.defineProperty(person, "age", { 17 get:function() { 18 return this._age; 19 } 20 }); 21 22 person.birthday = 1989; 23 console.log(person.age); // 28
寄存器允许我们对属性执行额外的操作。
class继承
还记得上一节笔记中我们是如何实现类和继承的么?我们需要编写一些额外的代码,并且需要正确实现原型链。
那么在ECMAScript6中,提供了class、super和extends关键字,帮助我们更加方便的实现类和继承,我们来看一个示例:
1 class Person { 2 constructor(name) { 3 this.name = name; 4 } 5 6 sayName() { 7 console.log(this.name); 8 } 9 } 10 11 class Student extends Person { 12 constructor(name, age) { 13 super(name); 14 this.age = age; 15 } 16 17 sayAll() { 18 super.sayName(); 19 console.log(this.age); 20 } 21 } 22 23 var student = new Student("Li Lei", 28); 24 student.sayAll(); 25 // Li Lei 26 // 28
比较一下就可以发现,class
的定义包含了构造函数constructor
和定义在原型对象上的函数hello()
(注意没有function
关键字),这样就避免了Student.prototype.sayAll = function () {...}
这样分散的代码。
最后,创建一个Student
对象代码和前面章节完全一样。
ES6引入的class
和原有的JavaScript原型继承有什么区别呢?实际上它们没有任何区别,class
的作用就是让JavaScript引擎去实现原来需要我们自己编写的原型链代码。简而言之,用class
的好处就是极大地简化了原型链代码。
你一定会问,class
这么好用,能不能现在就用上?
现在用还早了点,因为不是所有的主流浏览器都支持ES6的class。如果一定要现在就用上,就需要一个工具把class
代码转换为传统的prototype
代码,可以试试Babel这个工具。