var Dog = { name : 'dog', speak : function () { alert('Woof!'); } }; var dog = Object.create(Dog); dog.speak();
这里就是使用了这个方法创建了Dog的一个实例dog,这种方式的本质其实还是与我们之前讨论的使用空对象挂接原型对象是一样的,代码基本就像这样:
Object.create = function (o) { function F() {} F.prototype = o; return new F(); };
使用这种方式较之使用构造函数加new的方式来说更加简洁,但是不能设置私有和公开成员,而且实例成员在各个实例之间不共享,所以也有一定的局限性。
Object.create(prototype, descriptors) 参数 prototype:必需。 要用作原型的对象。 可以为 null,一般也可以在需要的时候使用getPrototypeOf方法获取。 descriptors: 可选。 定义的一组属性的对象字面量,该对象包含了每个属性的描述对象。 属性描述对象称为属性的attributes对象,其含有如下成员: value:表示该属性的值,默认为undefined。 writable:表示该属性的值(value)是否可以改变,默认为true。这个值如果是false的话,即使是继承的子类的话,也无法修改属性的值,这个也是讲得通的。 enumerable: 表示该属性是否可枚举,默认为true,也就是该属性会出现在for...in和Object.keys()等操作中。 configurable:表示“可配置性”,默认为true。如果设为false,表示无法删除该属性,也不得改变attributes对象(value属性除外),也就是configurable属性控制了attributes对象本身的可写性。 get:表示该属性的取值函数(getter),默认为undefined。 set:表示该属性的存值函数(setter),默认为undefined。 返回值 一个具有指定的内部原型且包含指定的属性(如果有)的新对象。
毫无疑问,这个函数最有意义的应该就是第二个参数:一组属性描述对象,这个在别的语言中非常有用的特性姗姗来迟,虽然在JavaScript中可以直接使用dog.age=2这种方式定义和修改属性,但是这种数据成员终究不满足封装性的需求,而且不满足属性本质上是函数这种惯例,在这个版本中满足封装性的属性(本质上是函数)终于出现在大家视野中了,我们看一个例子来了解一下如何定义一组属性:
var Shape = { twoDimensional: true, color: 'Red'}; var Square = Object.create(Shape, { // 简单的属性,不包含任何的其他逻辑的属性 type: { value: 'square', enumerable: true, writable: true }, // 复杂一点的属性,包含有一些逻辑的属性 size: { get: function() { return this.__size__;}, set: function(pSize) { if (pSize > 10) alert('Too large...'); // 其他的处理逻辑,例如简单的保存一下 this.__size__ = pSize; }, enumerable: true } }); var square = Object.create(Square); square.type = 'rectangle'; alert(square.type); square.size = 9; alert(square.size);
使用起来还是很简单的,但是有一点不太优雅的代码就是this.__size__这个保存属性值的成员,为了满足封装性,我们得使用一下闭包的特性了:
var Square = Object.create(Shape, (function () { var m_size; return { // 简单的属性,不包含任何的其他逻辑的属性 type: { value: 'square', enumerable: true, writable: true }, // 复杂一点的属性,包含有一些逻辑的属性 size: { get: function() { return m_size;}, set: function(pSize) { // 常见的条件检查 if (pSize > 10) alert('Too large...'); // 其他的处理逻辑,例如简单的保存一下 m_size = pSize; }, enumerable: true } }; }()));
把Square定义的那段代码换成上面这段,就有点样子了。
var Shape = { twoDimensional: true, color: 'Red'}; var square = Object.create(Shape); Object.defineProperty(square, 'type', { value : 'square', enumerable : true, writable: true }); square.type = 'rectangle'; alert(square.type);
使用defineProperties方法就与create中的第二个参数一致了,参见:
var Shape = { twoDimensional: true, color: 'Red'}; var square = Object.create(Shape); Object.defineProperties(square, (function () { var m_size; return { // 简单的属性,不包含任何的其他逻辑的属性 type: { value: 'square', enumerable: true, writable: true }, // 复杂一点的属性,包含有一些逻辑的属性 size: { get: function() { return m_size;}, set: function(pSize) { // 常见的条件检查 if (pSize > 10) alert('Too large...'); // 其他的处理逻辑,例如简单的保存一下 m_size = pSize; }, enumerable: true } }; }())); square.type = 'rectangle'; alert(square.type); square.size = 9; alert(square.size);
注意defineProperty/defineProperties这两个函数的返回值就是修改后的对象,有时候会使用这个返回值。
var Shape = { get type() { return this.__type__;}, set type(value) { this.__type__ = value;} }; var square = Object.create(Shape); square.type = 'rectangle'; alert(square.type);
简单吧,很简洁,非常强大。
var o = { name: 'frank'}; Object.defineProperties(o, { p1: { value: 1, enumerable: true }, p2: { value: 2, enumerable: false } }); alert(Object.keys(o)); alert(Object.getOwnPropertyNames(o));
注意name这种原始的属性是可枚举的,以前通过for/in结合hasOwnProperty方法才能完成枚举对象自身定义的属性的功能现在直接可以通过遍历keys完成了。
preventExtensions方法禁止对象添加新属性,isExtensible检测对象是否使用了preventExtensions方法。
seal方法禁止对象添加新属性和禁止删除已有属性,isSealed检测对象是否使用了seal方法。
freeze方法禁止对对象做任何修改,不能添加新属性和删除已有属性,也不能修改对象的属性值,isFrozen检测对象是否使用了freeze方法。
看到了吧,一个比一个严格,等到了使用freeze方法的时候,对象完全变成了只读对象,这个在很多场合下还是很有用的。看个简单的例子:
var o = { name: 'frank'}; Object.preventExtensions(o); o.age = 10; alert(o.age); //undefined,代表无法添加新属性 Object.seal(o); delete o.name; alert(o.name); // 删除不了 Object.freeze(o); o.name = 'dong'; alert(o.name); // 无法修改
var a = ['a','b','c','b']; alert(a.indexOf('b')); // 1 alert(a.indexOf('y')); // -1 alert(a.lastIndexOf('b')); // 3
2. every/some/forEach/filter/map/reduce/reduceRight
[1, 2, 3].map(function(item, index, arr){ return item * item; }); // [1, 4, 9]
通过函数的call方法,map方法可以用于字符串。
[].map.call('abc', function (x) { return x.toUpperCase() }) // [ 'A', 'B', 'C' ] // 或者 'abc'.split('').map(function (x) { return x.toUpperCase() }) // [ 'A', 'B', 'C' ]
forEach方法对所有元素依次执行一个函数,它与map的区别在于不返回新数组,而是对原数组的成员执行某种操作,甚至可能改变原数组的值。
[1, 2, 3].forEach(function(item, index, arr){ console.log("array[" + index + "] = " + item); }); // array[0] = 1 // array[1] = 2 // array[2] = 3
从上面代码可以看到,map和forEach的参数格式是一样的,都是一个函数。该函数接受三个参数,分别是当前元素、当前元素的位置(从0开始)、整个数组。
var out = []; [1, 2, 3].map(function(item, index, arr){ this.push(item * item); }, out); out // [1, 4, 9]
上面代码表示,如果提供一个数组作为第二个参数,则函数内部的this关键字就指向这个数组。
[1,2,3,4,5].filter(function(item){ return (item>3); }) // [4,5]
上面代码将大于3的原数组成员,作为一个新数组返回。
[1, 2, 3, 4, 5].filter(function(item, index, arr){ return index % 2 === 0; }); // [1, 3, 5]
上面代码返回原数组偶数位置的成员组成的新数组。
[1, 2, 3, 4, 5].some(function(item, index, arr){ return item >= 3; }); // 返回true
上面代码表示,如果存在大于等于3的数组成员,就返回true。
[1, 2, 3, 4, 5].every(function(item, index, arr){ return item >= 3; }); // 返回false
上面代码表示,只有所有数组成员大于等于3,才返回true。
用来累计的变量(即当前状态)
数组的当前元素
当前元素在数组中的序号(从0开始)
原数组
这四个参数之中,只有前两个是必须的,后两个则是可选的。
[1, 2, 3, 4, 5].reduce(function(x, y){ return x+y; }); // 15
上面代码的参数x表示累计变量,默认为0,y则是数组的当前元素。reduce方法依次将每个数组元素加入x,最终返回它们的总和15。
Array.prototype.sum = function (){ return this.reduce(function (partial, value){ return partial + value; }) }; [3,4,5,6,10].sum() // 28
如果要对累计变量指定初值,可以把它放在reduce方法的第二个参数。
[1, 2, 3, 4, 5].reduce(function(x, y){ return x+y; }, 10); // 25
上面代码指定参数x的初值为10,所以数组元素从10开始累加,最终结果为25。
function findLongest(entries) { return entries.reduce(function (longest, entry) { return entry.length > longest.length ? entry : longest; }, ''); }
var names = ['Collis', 'Cyan']; Array.isArray(names); // true
// 仅仅绑定了this,还需要主动的通过对象去调用 tooltip.show = showText.bind(tooltip); tooltip.show(); overlay.show = showText.bind(overlay); overlay.show(); // 直接调用的apply/call形式 showText.call(tooltip); showText.call(overlay);
Date.now() // 1364026285194类似的数字
如果需要更精确的时间,可以使用window.performance.now()。它提供页面加载到命令运行时的已经过去的时间,单位是浮点数形式的毫秒。
window.performance.now() // 21311140.415类似的数字
这一组方法可用于生成一种唯一的id标识。
alert(new Date().toJSON());
" hello world ".trim() // "hello world"