ES6 一些零碎的知识点

最后更新:
阅读次数:

一些内置对象的方法

  • String 对象
    • String.prototype.endsWith(searchStr [, position]): 判断当前字符串是否是以另外一个给定的子字符串结尾的
    • String.prototype.startsWith(searchStr [, position]): 判断当前字符串是否是以另外一个给定的子字符串开头的
    • String.prototype.includes(searchStr [, position]): 判断一个字符串是否包含在另一个字符串中(includes 方法最初名为 contains 方法,后来因为 bug,在 ES6 中正式改名为 includes)
    • String.prototype.repeat(count): 返回一个重复当前字符串若干次数的新字符串
let str = "/static/home.html";

str.endsWith(".html"); // true
str.startsWith("/"); // true
str.includes("home"); // true
let str = "abc";
str.repeat(3); // abcabcabc
  • Array 对象
    • Array.prototype.includes(searchElement): 判断一个数组是否包含一个指定的值(该方法属于 ES7)
    • Arrat.from(arrayLike): 将一个类数组对象或一个可遍历对象转换为一个数组
      • 类数组对象: 拥有一个 length 属性和若干个索引属性的任意对象
      • 可遍历对象: 比如 Map 和 Set
let arr = ["aaa", 23, 222];

arr.includes("23"); // false
arr.includes(23); // true
// 类数组对象
function func() {
console.log(arguments);
console.log(Array.from(arguments));
}
func(1, 2, 3);
// {
// '0': 1,
// '1': 2,
// '2': 3,
// callee: function func(),
// length: 3,
// Symbol(Symbol.iterator): function values(),
// __proto__: Object
// }
// [1, 2, 3]

// Set
let set = new Set([1, 2, 3]);
console.log(Array.from(set)); // [1, 2, 3]

// Map
let map = new Map([[1, 2], { name: "ppp" }]);
console.log(Array.from(map)); // [Array(2), Array(2)]
  • Number 对象
    • Number.parseInt(string[, radix]):根据给定的进制数把一个字符串解析成整数
    • Number.parseFloat(string):把一个字符串解析为一个浮点数
Number.parseInt === parseInt; // true
Number.parseFloat === parseFloat; // true
  • Math 对象
    • Math.trunc(value):将数字的小数部分去掉,只留整数部分
    • Math.cbrt(x):返回任意数字的立方根
    • Math.hypot([value1[,value2, …]]):返回它的所有参数的平方和的平方根
Math.trunc(12.02); // 12
Math.cbrt(27); // 3
Math.hypot(3, 4); // 5

新的运算符

  • ES7 指数运算符: **
2 ** 3; // 8
3 ** 2; // 9
  • 扩展运算符

扩展运算符(spread)是三个点 ...。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。

console.log(1, ...[2, 3, 4], 5); // 1 2 3 4 5
var arr1 = [12, "hello"];
var arr2 = [false, "world"];
var arr3 = [...arr1, ...arr2];
console.log(arr3); // [12, "hello", false, "world"]

函数相关

  • 函数的 rest 参数(形式为“…变量名”):用于获取函数的多余参数,这样就不需要使用 arguments 对象了

rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

// test 1
function f1(...values) {
console.log(values); // [1, 2, "percy", true]
}
f1(1, 2, "percy", true);

// test 2
function f2(num1, ...values) {
console.log(num1); // 1
console.log(values); // [2, "percy", true]
}
f2(1, 2, "percy", true);

// test 3
// rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错
function f3(num1, ...values, bool) { // 报错,Uncaught SyntaxError: Rest parameter must be last formal parameter
console.log(num1);
console.log(values);
console.log(bool);
}
f3(1, 2, "percy", true);
  • [new.target](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new.target) 判断函数是否是通过 new 关键字调用
function Person() {
if (!new.target) {
console.error("Person should be called with new operator");
return;
}

console.log("new Person() successful");
}

Person();
new Person();

对象相关

  • 更简洁的对象字面量写法

ES6 允许直接写入变量和函数,作为对象的属性和方法。

// 属性
let name = "percy";
let age = 21;
// 方法
let sayName = function() {
console.log(this.name);
};
let sayAge = () => {
console.log(this.age);
};

// 创建对象
let user = {
name,
age,
sayName,
sayAge,
sayAll() {
console.log(`name:${this.name},age:${this.age}`);
}
};

console.log(user); // Object {name: "percy", age: 21}
user.sayName(); // percy
user.sayAge(); // undefined
// 要是不理解上一行为什么是 undefined,可以去看看箭头函数是如何对待 this 的
user.sayAll(); // name:percy,age:21
  • 属性名表达式
let obj = {
// // ES6 新增写法
[1 + 2 + "a" + "b"]: "hell"
};

// 传统写法
obj.foo = true;

// ES6 新增写法
obj["a" + "bc"] = 123;

console.log(obj); // Object {3ab: "hell", foo: true, abc: 123}
  • Object.is(value1, value2):用来判断两个值是否是同一个值

传统的两种相等比较有一些缺陷

  • ==:先对它两边的操作数做隐式的类型转换,再进行比较
  • ===:认为 +0 和 -0 相等,NaN 和 NaN 不相等

而 Object.is() 就是为弥补这些缺陷设计出来的!

"" == false; // true
Object.is("", false); // false

+0 === -0; // true
NaN === NaN; // false
Object.is(+0, -0); // false
Object.is(NaN, NaN); // true
  • Object.assign(target, …sources):把任意多个的源对象自身的可枚举属性浅拷贝给目标对象,然后返回目标对象
let person1 = {
name1: "percy",
age1: "21",
isStudent: true
};

// 设置 age1 属性为不可枚举
Object.defineProperty(person1, "age1", { enumerable: false });

let person2 = {
name2: "zyj",
age2: 22,
sayName: function() {
console.log(this.name2);
}
};

// 设置 sayName 属性(或方法)为不可枚举
Object.defineProperty(person2, "sayName", { enumerable: false });

let person3 = {
name3: "bob",
age3: 23,
sayAge: function() {
console.log(this.age3);
}
};

let persons = Object.assign({ name: "persons" }, person1, person2, person3);

console.log(persons);
// persons = {
// age2: 22,
// age3: 23,
// isStudent: true,
// name: "persons",
// name1: "percy",
// name2: "zyj",
// name3: "bob",
// sayAge: function() {
// console.log(this.age3);
// }
// }

Object.assign 方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。

// 继续上面的代码
persons.sayAge === person3.sayAge; // true
  • ES6 中,共有 5 种方法可以遍历对象的属性
    • for…in:循环遍历对象自身的和继承的可枚举属性(不含 Symbol 类型的属性)
    • Object.keys(obj):返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 类型的属性)
    • Object.getOwnPropertyNames(obj):返回一个数组,包含对象自身的所有属性(不含 Symbol 类型的属性,不包含继承属性,但是包括不可枚举属性)
    • Object.getOwnPropertySymbols(obj):返回一个数组,包含对象自身的所有 Symbol 类型的属性(不包括继承的属性)
    • Reflect.ownKeys(obj):返回一个数组,包含对象自身的所有属性(包含 Symbol 类型的属性,还有不可枚举的属性,但是不包括继承的属性)

以上的 5 种方法遍历对象的属性,都遵守同样的属性遍历的次序规则

  • 首先遍历所有属性名为数值的属性,按照数字排序
  • 其次遍历所有属性名为字符串的属性,按照生成时间排序
  • 最后遍历所有属性名为 Symbol 值的属性,按照生成时间排序

从 ES6 起,对象的属性名有两种类型:一种是原来就有的字符串另一种就是新增的 Symbol 类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。

// 为 obj 设置 2 个继承属性
Object.prototype.inherit1 = "inherit111";
Object.prototype[Symbol("inherit2")] = "inherit222";

// a,b 为 Symbol 类型的属性名
let a = Symbol("a");
let b = Symbol("b");

let obj = {
[a]: "aSymbol",
[b]: "bSymbol",
name: "percy",
age: 20,
c: "cEnumFalse",
d: "dEnumFalse"
};

// 设置 c,d 为不可枚举的属性
Object.defineProperties(obj, {
c: { enumerable: false },
d: { enumerable: false }
});

// test 1
var arr = [];
for (let value in obj) {
arr.push(value);
}
console.log(arr);
// ["name", "age", "inherit1"]

// test 2
console.log(Object.keys(obj));
// ["name", "age"]

// test 3
console.log(Object.getOwnPropertyNames(obj));
// ["name", "age", "c", "d"]

// test 4
console.log(Object.getOwnPropertySymbols(obj));
// [Symbol(a), Symbol(b)]

// test 5
console.log(Reflect.ownKeys(obj));
// ["name", "age", "c", "d", Symbol(a), Symbol(b)]
  • 谈谈 __proto__ 属性

__proto__属性(前后各两个下划线),用来读取或设置当前对象的 prototype 对象(原型对象),该属性没有写入 ES6 的正文,而是写入了附录,原因是 __proto__ 前后的双下划线,说明它本质上是一个内部属性,而不是一个正式的对外的 API,只是由于浏览器广泛支持,才被加入了 ES6。标准明确规定,只有浏览器必须部署这个属性,其他运行环境不一定需要部署,而且新的代码最好认为这个属性是不存在的。因此,无论从语义的角度,还是从兼容性的角度,都不要使用这个属性,而是使用下面的 Object.setPrototypeOf()(写操作)、Object.getPrototypeOf()(读操作)、Object.create()(生成操作)代替。

  • Object.getPrototypeOf(object):(ES 5.1)返回指定对象的原型对象
  • Object.setPrototypeOf(obj, prototype):(ES 6)将一个指定的对象的原型设置为另一个对象或者 null
  • Object.create(proto, [ propertiesObject ]):(ES 5.1)创建一个拥有指定原型和若干个指定属性的对象
    • 注意: 上面这个函数的第二个参数要与 Object.defineProperties() 的第二个参数保持一致,否则报错
let obj = {
name: "percy",
age: 20
};
let objProto = {
where: "objProto",
sayName: function() {
console.log(this.name);
}
};

console.log(Object.getPrototypeOf(obj)); // Object {}
console.log(Object.getPrototypeOf(objProto)); // Object {}

Object.setPrototypeOf(obj, objProto);
Object.setPrototypeOf(objProto, null);

console.log(Object.getPrototypeOf(obj)); // Object {where: "objProto"}
console.log(Object.getPrototypeOf(objProto)); // null

let createObj = Object.create(objProto, { name: { value: "I am create!" } });
createObj.sayName(); // I am create!

参考资料