ES6 函数和对象(一)

JS对象复习

对象是JS十分灵活的数据类型,他可以作为“散列表”,还可以从原型对象中继承属性。

在JS中除了字符串,数字,true,false,undefined,null这些原始类型,其他都是对象。

我们可以创建对象,并设置,查找,删除,测试,枚举对象属性。

  1. 属性特性

对象中的属性也有一些与之相关的值,称为属性特性(property attribute)

属性特性有下列几个

  • 值(value)
  • 可写(writable attribute)
  • 可枚举(enumerable attribute)
  • 可配置(configurable attribute)

注意属性特性的默认值是value=undefined,writable=true,enumerable=true,configurable=true

  1. 对象特性

另外还有与对象相关的一些特性,称为对象特性属性(object attribute),如下:

  • prototype原型属性,指向另一个对象
  • class 标识对象类型的字符串
  • extensible flag扩展标记,指明是否可以向对象中添加新的属性
  1. 对象分类
  • 内置对象(native object) 由ECMAScript规定对象或类,例如,数组,函数,日期,正则表达式
  • 宿主对象(host object)由js解释器嵌入的宿主环境,DOM的对象就是宿主对象
  • 自定义对象(user-defined object),由程序员编写的对象

包装对象:原始值在设置,读取属性值,js会对原始对象进行临时的包装,形成包装对象,这样就可以对原始值对象进行某些操作了,注意原始值和包装对象是不同的,两者通过===比较是不相等的

  1. 创建对象
  • 对象直接量{}
var i = { x:1 }; 

每次{}都创建并初始化一个新的对象,并重新计算属性值

  • new
var i = new Object(); 

new 运算符创建并初始化一个新的对象,new后面跟一个函数调用,这个函数称为构造函数

  • Object.create()

Object.create()是由ES5定义的方法,它可以以第一个参数为原型,以第二个参数为配置参数创建一个对象

创建一个空的对象

var a = Object.create(Object.prototype); 

效果和

var a = {};
var a = new Object(); 

相似

也可以基于某个原型创建一个对象

var a =  {x:1, y:2};
var b = Object.create(a);
b.x
// 1
b.y
// 2 

还可以设置属性特性

var b = Object.create(a, {
x: {value:2, writeble: false, enumerable: true, configurable: true}
});

b.x
// 2
b.x = 1
// 1
b.x
// 2 现在无法修改x的值了 

不过,这个方法只支持chrome 5,firefox 4.0,IE9,Opera 11.60, Safari 5,这里有一个polyfill

if (typeof Object.create != 'function') {
 // Production steps of ECMA-262, Edition 5, 15.2.3.5
 // Reference: http://es5.github.io/#x15.2.3.5
 Object.create = (function() {
   //为了节省内存,使用一个共享的构造器
   function Temp() {}

   // 使用 Object.prototype.hasOwnProperty 更安全的引用 
   var hasOwn = Object.prototype.hasOwnProperty;

   return function (O) {
     // 1. 如果 O 不是 Object 或 null,抛出一个 TypeError 异常。
     if (typeof O != 'object') {
       throw TypeError('Object prototype may only be an Object or null');
     }

     // 2. 使创建的一个新的对象为 obj ,就和通过
     //    new Object() 表达式创建一个新对象一样,
     //    Object是标准内置的构造器名
     // 3. 设置 obj 的内部属性 [[Prototype]] 为 O。
     Temp.prototype = O;
     var obj = new Temp();
     Temp.prototype = null; // 不要保持一个 O 的杂散引用(a stray reference)...

     // 4. 如果存在参数 Properties ,而不是 undefined ,
     //    那么就把参数的自身属性添加到 obj 上,就像调用
     //    携带obj ,Properties两个参数的标准内置函数
     //    Object.defineProperties() 一样。
     if (arguments.length > 1) {
       // Object.defineProperties does ToObject on its first argument.
       var Properties = Object(arguments[1]);
       for (var prop in Properties) {
         if (hasOwn.call(Properties, prop)) {
           obj[prop] = Properties[prop];
         }
       }
     }

     // 5. 返回 obj
     return obj;
   };
 })();
} 
  1. 属性查询设置

可以通过[].两种方式获取和设置属性值

  1. 属性删除

通过delete运算符可以删除对象属性,不过delete只能删除自有属性,不能删除继承属性

delete不能删除可配置性为false的对象属性,例如某些内置对象的属性,或者是通过Object.defineProperty设置的

var a = {x:1};
Object.defineProperty(a, "x", {configurable:false})
delete a.x
// false 

只有这些情况,delete会返回false,删除不存在的属性,或者继承方法,虽然无法删除成功,但是都返回true

  1. 属性检测
  • in运算符
var a = {x:1};
"x" in a
// true
"toString" in a
// true 

很显然,in会沿着原型链查询属性

  • hasOwnProperty()
a.hasOwnProperty("x");
// true
a.hasOwnProperty("toString");
// false 

很显然,hasOwnProperty()只能查询本身的属性

  • propertyIsEnumerable()
var a = {x:1};
Object.defineProperty(a, "x", {enumerable: false});
"x" in a
// true
a.propertyIsEnumerable("x");
// false
var b = {x:1};
b.propertyIsEnumerable("x");
// true
b.propertyIsEnumerable("y");
false 

很显然propertyIsEnumerable()方法是用以检测属性是否能被枚举出来,所以连带也检测了属性存不存在

  • 属性查询检测
a.x!==undefined
// true 

这种方式也可以检测属性存不存在,但是如果值为undefined的情况下,这个就不起作用了

  1. 属性遍历

for in可以对对象进行遍历

var a = {x:1};
Object.defineProperty(a, "x", {enumerable: false});
var b = {x:1};
for(let i in a) {console.log(i);}
// 
for(var x in b) console.log(x);
// x 

ES5还提供了Object.keys()方法,返回可一个可枚举属性的数组

Object.keys(b);
// ["x"]

Object.keys(a);
// [] 

Object.getOwnPropertyName()不仅能获取可枚举的属性,还能获取不可枚举的属性

Object.getOwnPropertyNames(a)
// ["x"]

Object.getOwnPropertyNames(b)
// ["x"] 

这个方法我们无法通过polyfill实现

  1. 属性getter和setter

对象属性由名字,值和一组特性构成。而当中的值其实可以用getter和setter方法表示。而由getter和setter定义的属性称为“存取器属性(accessor properties)”。

当程序查询存取器属性时,JS调用getter无参数方法时,返回getter返回值;设置存取器属性时,会将赋值表达式右侧的值作为参数传入setter,忽略setter的返回值。

var a = { 
   width:10,
   height:10, 
   length:10, 
   get volume() { 
       return this.width*this.height*this.length; 
   }};
a.volume
// 1000
a.volume = 10;

a.volume
// 1000 

setter存取器属性不存在,无法对volume赋值

var a = {
   width:10, 
   height:10, 
   length:10, 
   get volume() { return this.width*this.height*this.length; }, 
   set volume(v) { this.width = Math.cbrt(v); this.height = this.width; this.length = this.width;}
};

a.volume = 27;
// 27
a.width
// 3
a.height
// 3
a.length
// 3 
  1. 属性特性
  • 获取属性描述符

可以通过Object.getOwnPropertyDescriptor获取对象中某个属性的属性描述符

Object.getOwnPropertyDescriptor(a, "volume");

{ get: [Function: get volume],
 set: [Function: set volume],
 enumerable: true,
 configurable: true }
 
 
Object.getOwnPropertyDescriptor(a, "width");


{ value: 10,
 writable: true,
 enumerable: true,
 configurable: true } 

很显然,存取器属性没有valuewritable特性只有getset特性

  • 设置属性特性

Object.defineProperty()和Object.defineProperties()分别可以对单个对象属性和多个对象属性进行设置

Object.defineProperty(a, "width", {writable:false});

a.volume = 27;
// 27
a.width
// 10 
Object.defineProperty(a, "width", {writable:true});
a.volume = 64;
// 64
a.width
// 4 

writable属性特性是控制对象属性是否能存储值

Object.defineProperty(a, "width", {configurable:false});
Object.defineProperty(a, "width", {writable:false});
a.volume = 27;
a.length
// 4 

configurable特性可以将writable特性设置为false,但是不能设置为true

Object.defineProperty(a, "width", {writable:true});
// Uncaught TypeError: Cannot redefine property: width 
Object.defineProperty(a, "width", {enumerable:false});

// Uncaught TypeError: Cannot redefine property: width 

但是configurable设为false之后就无法对enumerable进行设置

Object.defineProperty(a, "width", {configurable:true});
// Uncaught TypeError: Cannot redefine property: width 

当然也无法对configurable自身进行设置

Object.defineProperty(a, "volume", {configurable:false});
Object.defineProperty(a, "volume", {get: function(){ return this.width;}});

// Uncaught TypeError: Cannot redefine property: volume 

gettersetter存取属性也是不能被设置的

delete a.width
false 

删除就更不可能了

  1. 对象的三个属性
  • 原型属性 prototype

这个都应该知道了,我们可以通过制定prototype属性来指定一个对象作为父对象。

也可以通过a.isPrototype(b)来对象a是不是对象b的原型

var a = {};
var b = Object.create(a);
a.isPrototypeOf(b);
// true 

另外火狐和谷歌实现了一个__proto__可以通过这查询和设置对象原型

  • 类属性class

对象的类属性是一个字符串,用以表示对象的类型信息,可以通过Object.prototype.toString()方法间接获取对象的类型信息,自定义类的类属性一般都是Object,我们无法对class属性进行设置

  • 可扩展性
    对象的可扩展性用以表示是否可以给对象添加新属性。

在ES5之前自定义对象一直都是可扩展的,但是ES5定义了很多方法来查询,设置可扩展性

  1. 查询可扩展性

Object.isExtensible()

var a = {x:1};
Object.isExtensible(a); 
  1. 设置为不可扩展

此过程不可逆,通过Object.preventExtensible(),可以将传入的参数对象设置为不可扩展

Object.preventExtensions(a);
Object.isExtensible(a);
// false 

Object.seal()除了有设置不可扩展的功能外,还可以将所有自有属性设置为不可配置

Object.seal(a);
Object.defineProperty(a, "x", {configurable:true});
// Uncaught TypeError: Cannot redefine property: x
Object.isSealed(a); // 可以检测对象是否封闭
// true 

还有比这个方法更狠的Object.freeze(),除了拥有Object.seal()的功能外,它还可以将所有数据属性设置为只读

Object.freeze(a);
a.x
// 1
a.x = 2;
a.x
// 1 

待续