晴明的博客园 GitHub      CodePen      CodeWars     

[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]

 

posted @ 2016-05-13 14:51  晴明桑  阅读(1760)  评论(0编辑  收藏  举报