[ES6] 一些类型方法的扩展
#
Object
Object initializer
对象可以通过 new Object(), Object.create() 方法, 或者使用字面 标记 (初始化 标记)初始化。
对象初始化,由花括号{}包含的一个由0个或者多个对象属性名和关联值组成的列表构成。
#计算的属性名
从ES6开始,对象初始化语法开始支持计算的属性名。
其允许在[]中放入表达式,计算结果可以当做属性名。
这种用法和用方括号访问属性非常类似,用来读取和设置属性。
现在同样的语法也可以用于对象字面值
var i = 0;
var a = {
["foo" + ++i]: i,
["foo" + ++i]: i,
["foo" + ++i]: i
};
console.log(a.foo1); // 1
console.log(a.foo2); // 2
console.log(a.foo3); // 3
var param = 'size';
var config = {
[param]: 12,
["mobile" + param.charAt(0).toUpperCase() + param.slice(1)]: 4
};
console.log(config); // { size: 12, mobileSize: 4 }
Object.setPrototypeOf()
Object.setPrototypeOf(obj, prototype)
将一个指定的对象的原型设置为另一个对象或者null(既对象的[[Prototype]]内部属性).
如
果对象的[[Prototype]]被修改成不可扩展(通过 Object.isExtensible()查看),就会抛出
TypeError异常。如果prototype参数不是一个对象或者null(例如,数字,字符串,boolean,或者
undefined),则什么都不做。否则,该方法将obj的[[Prototype]]修改为新的值。
相对于 Object.prototype.__proto__ ,它被认为是修改对象原型更合适的方法
#添加原型链
通过Object.getPrototypeOf()和 Object.prototype.__proto__的组合,可以给一个新的原型对象添加完整的原型链
/**
*** Object.appendChain(@object, @prototype)
*
* Appends the first non-native prototype of a chain to a new prototype.
* Returns @object (if it was a primitive value it will transformed into an object).
*
*** Object.appendChain(@object [, "@arg_name_1", "@arg_name_2", "@arg_name_3", "..."], "@function_body")
*** Object.appendChain(@object [, "@arg_name_1, @arg_name_2, @arg_name_3, ..."], "@function_body")
*
* Appends the first non-native prototype of a chain to the native Function.prototype object, then appends a
* new Function(["@arg"(s)], "@function_body") to that chain.
* Returns the function.
*
**/
Object.appendChain = function(oChain, oProto) {
if (arguments.length < 2) {
throw new TypeError('Object.appendChain - Not enough arguments');
}
if (typeof oProto === 'number' || typeof oProto === 'boolean') {
throw new TypeError('second argument to Object.appendChain must be an object or a string');
}
var oNewProto = oProto,
oReturn = o2nd = oLast = oChain instanceof this ? oChain : new oChain.constructor(oChain);
for (var o1st = this.getPrototypeOf(o2nd);
o1st !== Object.prototype && o1st !== Function.prototype;
o1st = this.getPrototypeOf(o2nd)
) {
o2nd = o1st;
}
if (oProto.constructor === String) {
oNewProto = Function.prototype;
oReturn = Function.apply(null, Array.prototype.slice.call(arguments, 1));
this.setPrototypeOf(oReturn, oLast);
}
this.setPrototypeOf(o2nd, oNewProto);
return oReturn;
}
#一:给一个原型添加链
function Mammal() {
this.isMammal = 'yes';
}
function MammalSpecies(sMammalSpecies) {
this.species = sMammalSpecies;
}
MammalSpecies.prototype = new Mammal();
MammalSpecies.prototype.constructor = MammalSpecies;
var oCat = new MammalSpecies('Felis');
console.log(oCat.isMammal); // 'yes'
function Animal() {
this.breathing = 'yes';
}
Object.appendChain(oCat, new Animal());
console.log(oCat.breathing); // 'yes'
#二:将一个基本类型转化为对应的对象类型并添加到原型链上
function Symbol() {
this.isSymbol = 'yes';
}
var nPrime = 17;
console.log(typeof nPrime); // 'number'
var oPrime = Object.appendChain(nPrime, new Symbol());
console.log(oPrime); // '17'
console.log(oPrime.isSymbol); // 'yes'
console.log(typeof oPrime); // 'object'
#三:给函数类型的对象添加一个链,并添加一个新的方法到那个链上
function Person(sName) {
this.identity = sName;
}
var george = Object.appendChain(new Person('George'),
'console.log("Hello guys!!");');
console.log(george.identity); // 'George'
george(); // 'Hello guys!!'
Object.assign()
Object.assign(target, ...sources)
可以把任意多个的源对象所拥有的自身可枚举属性拷贝给目标对象,然后返回目标对象。
只会拷贝源对象自身的并且可枚举的属性到目标对象身上。
注意1,对于访问器属性,该方法会执行那个访问器属性的 getter 函数,然后把得到的值拷贝给目标对象,如果想拷贝访问器属性本身,使用 Object.getOwnPropertyDescriptor() 和 Object.defineProperties() 方法。
注意2,String类型和 Symbol 类型的属性都会被拷贝。
注意3,在属性拷贝过程中可能会产生异常,比如目标对象的某个只读属性和源对象的某个属性同名,这时该方法会抛出一个 TypeError 异常,
拷贝过程中断,已经拷贝成功的属性不会受到影响,还未拷贝的属性将不会再被拷贝。
注意4, Object.assign 会跳过那些值为 null 或 undefined 的源对象。
#克隆一个 object
var obj = { a: 1 }; var copy = Object.assign({}, obj); console.log(copy); // { a: 1 }
#合并 objects
var o1 = { a: 1 }; var o2 = { b: 2 }; var o3 = { c: 3 }; var obj = Object.assign(o1, o2, o3); console.log(obj); // { a: 1, b: 2, c: 3 } console.log(o1); // { a: 1, b: 2, c: 3 }, 注意目标对象自身也会改变。
#拷贝 symbol 类型的属性
var o1 = { a: 1 }; var o2 = { [Symbol("foo")]: 2 }; var obj = Object.assign({}, o1, o2); console.log(obj); // { a: 1, [Symbol("foo")]: 2 }
#继承属性和不可枚举属性是不能拷贝的
var obj = Object.create({foo: 1}, { // foo 是个继承属性。 bar: { value: 2 // bar 是个不可枚举属性。 }, baz: { value: 3, enumerable: true // baz 是个自身可枚举属性。 } }); var copy = Object.assign({}, obj); console.log(copy); // { baz: 3 }
#原始类型会被包装为 object
var v1 = "abc"; var v2 = true; var v3 = 10; var v4 = Symbol("foo") var obj = Object.assign({}, v1, null, v2, undefined, v3, v4); // 原始类型会被包装,null 和 undefined 会被忽略。 // 注意,只有字符串的包装对象才可能有自身可枚举属性。 console.log(obj); // { "0": "a", "1": "b", "2": "c" }
#异常会打断接下来的拷贝任务
var target = Object.defineProperty({}, "foo", { value: 1, writeable: false }); // target 的 foo 属性是个只读属性。 Object.assign(target, {bar: 2}, {foo2: 3, foo: 3, foo3: 3}, {baz: 4}); // TypeError: "foo" is read-only // 注意这个异常是在拷贝第二个源对象的第二个属性时发生的。 console.log(target.bar); // 2,说明第一个源对象拷贝成功了。 console.log(target.foo2); // 3,说明第二个源对象的第一个属性也拷贝成功了。 console.log(target.foo); // 1,只读属性不能被覆盖,所以第二个源对象的第二个属性拷贝失败了。 console.log(target.foo3); // undefined,异常之后 assign 方法就退出了,第三个属性是不会被拷贝到的。 console.log(target.baz); // undefined,第三个源对象更是不会被拷贝到的。
#拷贝访问器(accessor)
var obj = { foo: 1, get bar() { return 2; } }; var copy = Object.assign({}, obj); console.log(copy); // { foo: 1, bar: 2 }, copy.bar的值来自obj.bar的getter函数的返回值 // 下面这个函数会拷贝所有自有属性的属性描述符 function completeAssign(target, ...sources) { sources.forEach(source => { let descriptors = Object.keys(source).reduce((descriptors, key) => { descriptors[key] = Object.getOwnPropertyDescriptor(source, key); return descriptors; }, {}); // Object.assign 默认也会拷贝可枚举的Symbols Object.getOwnPropertySymbols(source).forEach(sym => { let descriptor = Object.getOwnPropertyDescriptor(source, sym); if (descriptor.enumerable) { descriptors[sym] = descriptor; } }); Object.defineProperties(target, descriptors); }); return target; } var copy = completeAssign({}, obj); console.log(copy); // { foo:1, get bar() { return 2 } }
Object.is()
Object.is(value1, value2);
用来判断两个值是否是同一个值。
Object.is() 会在下面这些情况下认为两个值是相同的:
两个值都是 undefined
两个值都是 null
两个值都是 true 或者都是 false
两个值是由相同个数的字符按照相同的顺序组成的字符串
两个值指向同一个对象
两个值都是数字并且
都是正零 +0
都是负零 -0
都是 NaN
都是除零和 NaN 外的其它同一个数字
这种相等性判断逻辑和传统的 == 运算符所用的不同,== 运算符会对它两边的操作数做隐式的类型转换(如果它们是不同类型的值的话),
然后才进行相等性比较,(所以才会有类似 "" == false 为 true 的现象),但 Object.is 不会做这种类型转换。
而且,即便严格相等运算符 === 不会对操作数进行类型转换,但它不能区分两个不同的数字 -0 和 +0,还会把两个 NaN 看成是不相等的。
#
Object.is('foo', 'foo'); // true Object.is(window, window); // true Object.is('foo', 'bar'); // false Object.is([], []); // false var test = { a: 1 }; Object.is(test, test); // true Object.is(null, null); // true // 两个特例,=== 也没法判断的情况 Object.is(0, -0); // false Object.is(NaN, 0/0); // true
#
string
String.raw()
String.raw(callSite, ...substitutions)
String.raw() 是一个模板字符串的标签函数,
它的作用类似于 Python 中的字符串前缀 r 和 C# 中的字符串前缀 @,
是用来获取一个模板字符串的原始字面量值的。
String.raw() 是唯一一个内置的模板字符串标签函数。
#
console.log(String.raw `Hi\n!`); // "Hi\\n!",这里得到的不是 Hi 后面跟个换行符,而是跟着 \ 和 n 两个字符 console.log(String.raw `Hi\u000A!`); // "Hi\\u000A!",同上,这里得到的会是 \、u、0、0、0、A 6个字符, // 任何类型的转义形式都会失效,保留原样输出 let name = "Bob"; console.log(String.raw `Hi\n${name}!`); // "Hi\\nBob!",内插表达式还可以正常运行 console.log(String.raw({raw: "test"}, 0, 1, 2)); // "t0e1s2t",通常不需要把它当成普通函数来调用
#
array
Array.prototype.entries()
arr.entries()
entries() 方法返回一个 Array Iterator 对象,该对象包含数组中每一个索引的键值对。
#
var arr = ['a', 'b', 'c']; var eArr = arr.entries(); console.log(eArr.next().value); // [0, 'a'] console.log(eArr.next().value); // [1, 'b'] console.log(eArr.next().value); // [2, 'c'] //using a for…of loop: var arr = ['a', 'b', 'c']; var eArr = arr.entries(); for (let e of eArr) { console.log(e); }
#
Array.from()
Array.from(arrayLike[, mapFn[, thisArg]])
Array.from() 方法可以将一个类数组对象或可迭代对象转换成真正的数组。
在 ES6 中, Class 语法允许为内置类型(比如 Array)和自定义类新建子类(比如叫 SubArray)。
这些子类也会继承父类的静态方法,比如 SubArray.from(),调用该方法后会返回子类 SubArray 的一个实例,而不是 Array 的实例。
#
// 将类数组对象(arguments)转换成数组 (function () { var args = Array.from(arguments); console.log(args);// [1, 2, 3] return args; })(1, 2, 3); // 将可迭代对象(Set 对象)转换成数组 console.log(Array.from(new Set(["foo", window]))); // ["foo", window] // Map 对象也可以 var m = new Map([[1, 2], [2, 4], [4, 8]]); console.log(Array.from(m)); // [[1, 2], [2, 4], [4, 8]] // 字符串对象既是类数组又是可迭代对象 console.log(Array.from("foo")); // ["f", "o", "o"] // 使用 map 函数转换数组元素 console.log(Array.from([1, 2, 3], x => x + x)); // [2, 4, 6] // 生成一个数字序列 console.log(Array.from({length:5}, (v, k) => k)); // [0, 1, 2, 3, 4]