es6的一些笔记

关于es6的一些笔记:

编程虐我千百遍,我待编程如初恋。
看了不下5遍es6,
马什么梅?什么冬梅?马冬什么?奥,马冬梅啊。
关闭教程之后,马什么梅?
马..马…马了个巴子的…又tm忘了。
如果你没有过目不忘的本领,个人总结的记忆的强弱:
调试bug>实践>自己写一遍>看一遍。
下面是我看着阮一峰老师的es6入门叫声然后自己动手写的一些笔记,当然也有一部分直接拉取的阮一峰老师的。
如果想看详细教程:请看es6阮一峰日志,传送门:
es6 阮一峰

正文

1.let不存在变量提升。但是var有变量提升。
2.区级作用域{ }类型中如果有let或者const,此区块不再受到外部影响。

var tmp = 123;
if(ture){
    tmp = 'abc'; //ReferenceError;
    let tmp;
}

3.let不允许重复声明
4.es5规定,函数只能在顶层作用域和函数作用域之中声明,不能再块级作用域声明,但是浏览器没有遵守这个规定,为了兼容以前的额旧代码,还是支持在块级作用域声明函数,es6明确允许在块级作用域之中声明函数,函数声明语句类似于let,再块级作用域之外不可引用。
5.const一旦声明变量,就必须立即初始化。同时,const也不存在变量提升,必须在声明后使用,和let一样,不可重复声明。
6.for in 循环遍历对象 简单示例

例如
var obj ={
    a:'123',
    b:'asdv',
    c:123
}
for(let i in obj){
    //遍历时候的i是对象名字 也是 a b c
    obj[i]//这取出来的是对象的值
}

Object.keys() 简单示例

var obj ={
    a:'123',
    b:'asdv',
    c:123
}
Object.keys(obj) //返回一个数组 ['a','b','c']
//然后我们forEach一下
Object.keys(obj).forEach(function(key,i){
    key  //a  b c
    i    //0  1 2
    obj[key] //'123' 'asdv' 123
})

详细区别传送门:for in 和 object.keys的详细区别
for in 会遍历自定义的枚举属性,以及原型链上的可枚举属性
某些情况下可能按照随机顺序遍历数组元素。
object.keys 会遍历自身可枚举属性(返回结果为数组),数组中的属性名排列顺序与使用for in 循环遍历该对象时返回的顺序一致与for in的主要区别在于不能遍历出原形链上的属性。
7.顶层对象,在浏览器环境指的是window对象,在Node指的是global对象。es5之中,顶层对象的属性与全局变量是等价的。
es6为了改变这一点,同时又为了保持兼容性,var命令,function命令声明的全局变量依旧是顶层对象的属性,另一方面规定:let,const,class命令声明的全局变量,不属于顶层对象的属性,

var a = 1;
// 如果在 Node 的 REPL 环境,可以写成 global.a
// 或者采用通用方法,写成 this.a
window.a // 1

let b = 1;
window.b // undefined

8.解构赋值的例子
es6允许按照一定的模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。
其实只要按照模板往里面放入值就行,只要格式一样就能赋值,非常简便。
等号右边的数据结构必须具有Iterator接口
如果赋值不成功,变量的值为undefined
其中三个点(…)这个代表数组

let [a, b, c] = [1, 2, 3];
let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3

let [ , , third] = ["foo", "bar", "baz"];
third // "baz"

let [x, , y] = [1, 2, 3];
x // 1
y // 3

let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]

let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []

一些不完全解构的例子:
即使模板不用,也会把相同的部分赋值上去

let [x, y] = [1, 2, 3];
x // 1
y // 2

let [a, [b], d] = [1, [2, 3], 4];
a // 1
b // 2
d // 4

解构赋值还可以有他自己的默认值

let [foo = true] = [];
foo // true

let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'

es6内部使用严格相等运算符(===)判断一个位置是否有值,所以只有当一个数组成员严格等于undefined时候,默认值才会生效。

let [x = 1] = [undefined];
x // 1

let [x = 1] = [null];
x // null

变量的解构赋值还可以是表达式,但是表达式是惰性求值的,只有在用到的时候才会求值。
默认值还可以引用解构赋值的其他变量,但该变量必须已经声明。

let [x = 1, y = x] = [];     // x=1; y=1
let [x = 1, y = x] = [2];    // x=2; y=2
let [x = 1, y = x] = [1, 2]; // x=1; y=2
let [x = y, y = 1] = [];     // ReferenceError: y is not defined

9.对象的解构赋值例子
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。

let { bar, foo } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"

let { baz } = { foo: "aaa", bar: "bbb" };
baz // undefined

如果变量名与属性名不一致,必须写成下面这样。

let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"

let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj;
f // 'hello'
l // 'world'

如果要将一个已经声明的变量用于解构赋值,必须非常小心。

// 错误的写法
let x;
{x} = {x: 1};
// SyntaxError: syntax error

上面代码的写法会报错,因为 JavaScript 引擎会将{x}理解成一个代码块,从而发生语法错误。只有不将大括号写在行首,避免 JavaScript 将其解释为代码块,才能解决这个问题。

// 正确的写法
let x;
({x} = {x: 1});

对象的解构赋值,可以很方便地将现有对象的方法,赋值到某个变量。

let { log, sin, cos } = Math;

和数组解构一样,对象解构赋值也是可以允许有默认值的,匹配的值的时候也是根据(===)来匹配的,如果位置上为undefined,则判定此位置没有值,同时也是可以嵌套赋值的,这里不再过多的赘述。
如果想更多的了解点击传送门:

对象的解构赋值:详细


10字符串的解构赋值
字符串也可以解构赋值。这是因为此时字符串转换成了一个类似数组的对象

const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"

类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值

let {length : len} = 'hello';
len // 5

11数值和布尔值的赋值解构
解构规则:只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错。

let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError

数值和布尔值的例子:

let {toString: s} = 123;
s === Number.prototype.toString // true

let {toString: s} = true;
s === Boolean.prototype.toString // true

因为转化为对象后,Object中有toString属性,所以变量s都能取到值。


12.函数参数的解构赋值
函数的参数也可以使用解构赋值

function() add([x,y]){
    return x+y;
}
add([1,2]);

关于函数的解构赋值,阮一峰老师的教程中还有一个例子,有点看不明白,先记下来。

function move({x = 0, y = 0} = {}) {
  return [x, y];
}

move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]
function move({x, y} = { x: 0, y: 0 }) {
  return [x, y];
}

move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, undefined]
move({}); // [undefined, undefined]
move(); // [0, 0]

这是阮一峰老师对这个代码的解释:上面代码是为函数move的参数指定默认值,而不是为变量x和y指定默认值,所以会得到与前一种写法不同的结果。

除此之外:undefined也会触发函数参数的默认值;


13.关于圆括号的问题
解构赋值虽然方便了很多,但是对于编译器来说,一个式子到底是模式,还是表达式,没办法重一开始就知道,必须解析到(或者解析不到)等号才能知道
所以建议:只要有可能,就不要再模式中放置圆括号,
ps:个人理解,其实这个模式就是咱们赋值语句的模板,也就是对象中的属性值。
不能使用圆括号的例子:
(1)变量声明语句

let [(a)] = [1];

let {x: (c)} = {};
let ({x: c}) = {};
let {(x: c)} = {};
let {(x): c} = {};

let { o: ({ p: p }) } = { o: { p: 2 } };

(2)函数参数
函数参数也属于变量声明,因此不能带有圆括号

// 报错
function f([(z)]) { return z; }
// 报错
function f([z,(x)]) { return x; }

(3)赋值语句的模式

// 全部报错
({ p: a }) = { p: 42 };
([a]) = [5];
// 报错
[({ p: a }), { x: c }] = [{}, {}];

可以使用圆括号的例子只有一种情况:赋值语句的非模式部分,可以使用圆括号。

[(b)] = [3]; // 正确
({ p: (d) } = {}); // 正确
[(parseInt.prop)] = [3]; // 正确

这三个圆括号都是合法的,首先都是赋值语句,而且不是声明语句,其次它们的圆括号都不属于模式的一部分,第一个数组的解构赋值,所以不存在对象的属性值(模式)(其实数组也是有属性值的,那就是0 1 2 3 这里就不算了)
第二行语句中,是对象的解构赋值,但是模式是p,而值是d,所以对d用圆括号没问题,第三个和第一个一样。


14,关于解构赋值的一些用处。
(1)首先是交换变量的值

//以前最熟悉的写法
var a = 1;
var b = 2;
var temp;
temp = a;
a = b;
b = temp;
//有了解构赋值以后
var a = 1;
var b = 2;
[a,b] = [b,a]

(2)然后是从函数返回多个值
函数一次只能返回一个值,以前如果要返回多个值的话,就要把数据放到数组中,然后返回这个数组或者一个对象中,然后取出来的时候非常的麻烦,有了解构赋值取出这些数据就变得非常的简单了。

//数组的例子
function example(){
    return [1,2,3]
}
let [a,b,c] = example();
//对象的例子
function example(){
    return {
        foo:1,
        bar:2
    }
}
let {foo,bar} = example();

(3)函数参数的定义
解构赋值可以方便的将一组参数与变量名对应起来

//如果参数是有次序的值
function([x,y,z]){...}
function([1,2,3]);
//如果参数不是有次序的值,可以利用对象的解构赋值
function({x,y,z}){...}
function({z:3,y:2,x:1});

(4)提取JSON对象中的数据,尤其有用。

let jsonData = {
  id: 42,
  status: "OK",
  data: [867, 5309]
};

let { id, status, data: number } = jsonData;

console.log(id, status, number);
// 42, "OK", [867, 5309]

(5)函数参数的默认值

jQuery.ajax = function (url, {
  async = true,
  beforeSend = function () {},
  cache = true,
  complete = function () {},
  crossDomain = false,
  global = true,
  // ... more config
} = {}) {
  // ... do stuff
};

字符串

凡事有??的乱码,均是一个码点大于0xFFFF的字符。

15.javascript内部,字符以UTF-16的格式存储,每个字符固定两个字节,对于那些需要4个字节存储的字符(unicode码点大于0xFFFF的字符)javascript会认为他们是两个字符。


var s= '?'; //码点大于0xFFFF 注意不是吉祥的吉
s.length // 2
s.charAt(0) // ''
s.charAt(1) // ''
s.charCodeAt(0) // 55362
s.charCodeAt(1) // 57271

16.字符串的遍历
es6为字符串添加了遍历器接口,使得字符串可以被for of 循环遍历。

for (let codePoint of 'foo') {
  console.log(codePoint)
}
// "f"
// "o"
// "o"

当然for in 也可以遍历,只不过要麻烦一些

var str = 'foo';
for (let key in str) {
  console.log(str[key]);
}

但是for of最大的优点就是识别大于0xFFFF的码点,传统的for循环无法识别这样的码点。

let text = String.fromCodePoint(0x20BB7);
//这个码点大于0xFFFF所以占用4个字节。

for (let i = 0; i < text.length; i++) {
  console.log(text[i]);
}
// " "
// " "

for (let i of text) {
  console.log(i);
}
// "?"

17.es5提供charAt()方法,返回字符串给定位置的字符串,但是该方法不能识别大于0xFFFF的字符。

'abc'.charAt(0) // "a"
'?'.charAt(0) // "\uD842"

charAt方法期望返回的是用2个字节表示的字符,但是汉字‘?’占用了4个字节,charAt(0)表示获取的4个字节中的前两个字节,很显然这是没法显示的。
解决的办法是at()方法。可以识别unicode编号大于0xFFFF的字符,返回正确的字符串

'abc'.at(0) // "a"
'?'.at(0) // "?"

18.一般来说判断一个字符串是否包含在另一个字符串中,javascript只提供了indexOf()方法(注意大小写)
在es6中又提供了三种新的方法。
(1) includes() 返回布尔值,表示是否找到了参数字符串。
(2) startsWith () 返回布尔值,表示参数字符串是否在原字符串的头部。
(3) endsWith () 返回布尔值,表示参数字符串是否在原字符串的尾部。

let s = 'Hello world!';

s.startsWith('Hello') // true
s.endsWith('!') // true
s.includes('o') // true

这三个方法都支持第二个参数,表示开始搜索的位置

let s = 'Hello world!';

s.startsWith('world', 6) // true
s.endsWith('Hello', 5) // true //注意这个是倒着数的。
s.includes('Hello', 6) // false

19.repeat()
repeat方法返回一个将原字符串重复n次的字符串。

'x'.repeat(3) // "xxx"
'hello'.repeat(2) // "hellohello"
'na'.repeat(0) // ""

规则:
1 参数如果是小数会被取整(个人理解:向上取整)
2 参数如果是负数或者Infinity(无穷),会报错
3 但是如果参数是0到-1之间的小数,则等同于0,这是因为会先进行取整运算。0到-1之间的小数,取整以后等于-0,repeat视同为0
4 参数NaN等同于0
5 如果repeat的参数是字符串,则会先转化为数字。

'na'.repeat(2.9);
'na'.repeat(Infinity)
// RangeError
'na'.repeat(-1)
// RangeError
'na'.repeat(-0.9) // ""
'na'.repeat(NaN) // ""
'na'.repeat('na') // ""
'na'.repeat('3') // "nanana"

20 padStart()      padEnd()
这是补全字符串的函数,如果某个字符串不够制定长度,会在头部或者尾部补全,padStart()用于头部补全,padEnd()用于尾部补全。

'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'

'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'

规则:
1 如果原字符串的长度,等于或者大于制定的最小长度,则返回原字符串。
2 如果用来补全的字符串与原字符串,两者的长度之和,超过了制定的最小长度,则会截去超出位数的额补全字符串。
3 如果省略第二个参数,则会默认使用空格补全长度。

'xxx'.padStart(2, 'ab') // 'xxx'
'xxx'.padEnd(2, 'ab') // 'xxx'
'abc'.padStart(10, '0123456789')
// '0123456abc'
'x'.padStart(4) // '   x'
'x'.padEnd(4) // 'x   '

常见用途
1 为数值补全指定位数

'1'.padStart(10, '0') // "0000000001"
'12'.padStart(10, '0') // "0000000012"
'123456'.padStart(10, '0') // "0000123456"

2 填充固定格式的字符串

'12'.padStart(10, 'YYYY-MM-DD') // "YYYY-MM-12"
'09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"

21模板字符串
以前的javascript语言,输出模板通常是这样的:
相当麻烦

$('#result').append(
  'There are <b>' + basket.count + '</b> ' +
  'items in your basket, ' +
  '<em>' + basket.onSale +
  '</em> are on sale!'
);

es6引入了模板字符串来解决这个问题

$('#result').append(`
  There are <b>${basket.count}</b> items
   in your basket, <em>${basket.onSale}</em>
  are on sale!
`);

规则:
1 两端使用反引号来标示,在英文输入法下按键1的左边的那个按键。
2 可以当作普通字符串来使用,也可以定义多行字符串,也可以在字符串中    嵌入变量
3 如果想使用反引号,前面加\转义
4 使用模板字符串的时候,所有空格和缩进都会被保留在输出之中。
5 模板字符串之中的嵌入变量,需要将变量名写在${ }之中。大括号之中可以进行运算,以及对象属性的引用。还能调用函数。如果大括号之中是字符串,将会原样输出。
6 如果大括号之中的值不是字符串,将按照一定的规则转化为字符串。比如,大括号中是一个对象,将默认调用对象的toString方法。
7 模板字符串还可以嵌套
如果想看具体实例和更详细的解说传送门:阮一峰:模版字符串


22 正则的扩展


23 es6 最好使用0b(0B)或者0o(0O)表示二进制和八进制,在严格模式下八进制不再使用以前的0表示了,并且会报错。
如果要将0b和0o前缀的字符串转为十进制,要使用Number方法

Number('0b111')  // 7
Number('0o10')  // 8

24 Number.isFInite()   Number.isNaN()
这两个方法都是在Number类上添加的方法,所以引用的话,只能Number.xxx();
Number.isFlnite()用来检查一个数值是否为有限的,即不是Infinity注意参数类型不是数字,一律返回false;
Number.isNaN()用来检查一个值是否为NaN,同样如果是非数字,则一律返回false;
1 isFinite()
2 isNaN()
3 Number.isFinite();
4 Number.isNaN();
全局函数1 2 和Number类上的函数3 4的区别在于传统的方法1 2 先调用Number将非数字的值转化为数值,在进行判断,而3 4这两个新方法只对数值有效,Number.isFinite()对于非数值一律返回false, Number.isNaN()只有对于NaN才返回true,非NaN一律返回false。


25.es6将全局方法parseInt()和parseFloat()移植到Number对象上面,行为完全保持不变。这样做的目的是使语言逐步模块化


26.Number.isInteger()用来判断一个数值是否为整数。在javascript中,整数和浮点数采用的是同样的储存方法,所以25和25.0被视为同一个值,如果参数不是数值,Number.isInteger() 返回false

Number.isInteger(25) // true
Number.isInteger(25.1) // false
Number.isInteger(25) // true
Number.isInteger(25.0) // true

javascript采用的是IEEE754标准,数值的储存为64位双精度格式,数值精度最多可以达到53个二进制(1个隐藏位和52个有效位),如果数值的精度超过这个限度,第54位后面的位就会被舍弃,这个时候,Number.isInteger()可能会误判

Number.isInteger(3.0000000000000002)

上面代码中,Number.isInteger的参数明明不是整数,但是会返回true。原因就是这个小数的精度达到了小数点后16个十进制位,转成二进制位超过了53个二进制位,导致最后的那个2被丢弃了。
类似的情况还有,如果一个数值的绝对值小于Number.MIN_VALUE(5E-324),即小于 JavaScript 能够分辨的最小值,会被自动转为 0。这时,Number.isInteger也会误判。
总之,如果对数据精度的要求较高,不建议使用Number.isInteger()判断一个数值是否为整数


27.es6在Math对象上又扩展17个方法。这些方法都是静态的,只能在Math对象上调用。
ps:es6中加了一个指数运算符(**);
这里我就不记了,给个阮一峰老师的传送门:阮一峰:Math对象的扩展


28.函数参数的默认值
es6允许为函数的参数设置默认值,即直接写在参数定义的后面。

function log(x,y='world'){
    console.log(x,y='world');
}

好处:1,阅读代码的人,可以立即意识到哪些参数是可以省略的,不用查看函数体或者文档,其次,有利于将来的代码优化,即使未来的版本在对外接口中,彻底拿掉这个参数,也不会导致以前的代码无法运行。
参数变量是默认声明的,所以不能用let或者const再次声明

function foo(x = 5) {
  let x = 1; // error
  const x = 2; // error
}

使用参数默认值时,参数不能有同名参数。js没有函数的重载

// 不报错
function foo(x, x, y) {
  // ...
}

// 报错
function foo(x, x, y = 1) {
  // ...
}
// SyntaxError: Duplicate parameter name not allowed in this context

函数参数的默认值是惰性求值的。

let x = 99;
function foo(p = x + 1) {
  console.log(p);
}
foo() // 100
x = 100;
foo() // 101

29.参数解构赋值和函数默认值的结合
注意一个例子

function m1({x = 0, y = 0} = {}) {
  return [x, y];
}

// 写法二
function m2({x, y} = { x: 0, y: 0 }) {
  return [x, y];
}
// 函数没有参数的情况
m1() // [0, 0]
m2() // [0, 0]

// x 和 y 都有值的情况
m1({x: 3, y: 8}) // [3, 8]
m2({x: 3, y: 8}) // [3, 8]

// x 有值,y 无值的情况
m1({x: 3}) // [3, 0]
m2({x: 3}) // [3, undefined]

// x 和 y 都无值的情况
m1({}) // [0, 0];
m2({}) // [undefined, undefined]

m1({z: 3}) // [0, 0]
m2({z: 3}) // [undefined, undefined]

30.参数默认值的位置最好是尾参数。


31.函数的length属性
指定了默认值之后,函数的length属性,将返回没有指定默认值的参数个数。

(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2

length的含义是:预期传入的参数个数,某个参数指定默认值后,预计传入的参数个数也就不包括这个参数了。

(function (a = 0, b, c) {}).length // 0
(function (a, b = 1, c) {}).length // 1

如果函数的默认值不是尾参数,那么length将忽略设有默认值参数之后的参数。


32.函数的rest参数
es6引入rest参数用于获取函数多余的参数,这样就不需要arguments对象了,rest是一个真正的数组,所有数组的方法都可以使用,arguments变量是一个类数组,通过Array.prototype.slice.call(arguments)可以将arguments转化为数组,注意rest参数必须是最后一个参数,之后不能有其他参数,否则会报错,函数的length属性,不包括rest参数。


33.函数的严格模式
es5开始,函数内部就可以定义为严格模式,es6做了一点修改,规定:只要函数参数使用了默认值,解构赋值,或者扩展运算符,那么函数内部就不能显示的设置为严格模式,否则会报错。
这是因为:函数内部的严格模式,同样适用于函数体和函数参数,但是函数执行的时候,先执行函数参数,在执行函数体,这样就有不合理的地方,只有从函数体之中,才能知道是否以严格模式执行,但是参数却先于函数体执行。
两种方法可以规避这种限制,第一种是设定全局性的严格模式,这是合法的。

'use strict';

function doSomething(a, b = a) {
  // code
}

第二种是把函数包在一个无参数的立即执行函数里面,然后在外层函数使用严格模式。

const doSomething = (function () {
  'use strict';
  return function(value = 42) {
    return value;
  };
}());

34.函数的name属性
函数的name属性返回该函数的函数名,这个属性早就被浏览器广泛支持,但是知道es6才写入标准。
es6对这个属性的行为做了一些修改,如果将一个匿名函数赋值给一个变量,es5的name属性,会返回空字符串,而es6的name属性会返回实际的函数名。

var f = function () {};

// ES5
f.name // ""

// ES6
f.name // "f"

如果将一个有函数名字的函数赋值给一个变量,es5和es6都会返回这个具名函数的原名

const bar = function baz() {};

// ES5
bar.name // "baz"

// ES6
bar.name // "baz"

Function构造函数返回的函数实例,name属性的值为anonymous。

(new Function).name // "anonymous"

bind返回的函数,name属性值会加上bound前缀。

function foo() {};
foo.bind({}).name // "bound foo"

(function(){}).bind({}).name // "bound "

35.箭头函数
es6允许使用箭头定义函数

var f = v =>v;
//等同于
var f = function(v){
    return v;
}

注意:
1.箭头函数不需要参数或者有多个参数,使用一个圆括号表示参数部分
2.如果箭头函数的代码块部分多于一条语句,就要使用大括号将他们括起来,并且使用return语句返回。如果你返回的是一个对象,必须在对象外面加上括号,否则会报错。
3.普通函数中,函数体内的this对象就是使用时所在的对象,但是在箭头函数中,就是定义时所在的对象这事固定不变的。
4.箭头函数不可以当构造函数,也就是说不能使用new命令,否则会报错。
5.不可以使用arguments对象,该对象在函数体内不存在,如果要用,可以用rest参数代替。
6.不可以使用yield命令,因此箭头函数不能用作Generator函数。

//关于函数体内部this指向的问题。
function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}

var id = 21;

foo.call({ id: 42 });
// id: 42

36.双冒号运算符
箭头函数可以绑定this对象,大大减少了显示绑定this对象的写法(call、apply、bind、)但是箭头函数并不适合所有场合,所以提出了‘函数绑定’运算符,用来取代call、apply、bind调用。
规则:1.函数绑定运算符(::)双冒号左边是对象,右边是一个函数,该运算符号,会自动将左边的对象,所有上下文环境(this对象),绑定到右边的函数上面。
2.如果左边为空,右边是一个对象的方法,则等于将该方法,绑定在该对象上面。
3.如果双冒号运算符的运算结果还是一个对象,就可以采用链式写法。


37.数组的扩展运算符号
扩展运算符是三个点(…)。它好比是rest参数的逆运算,将一个数组转为用逗号分隔的参数序列
数组的扩展运算符可以展开数组,所以不再需要apply方法,将数组转为函数的参数了。
扩展运算符的应用:
1.复制数组
2.合并数组
3.与解构赋值的结合
4.与字符串的结合使用
5.实现Iterator接口的对象


38.Array.from()
用于将两类对象转为真正的数组:类似数组的对象和可遍历的对象
扩展运算符号也可以将某些数据结构转为数组,它调用的是遍历器解构,如果一个对象没有部署这个接口,就无法转换,Array.from对任何有length属性的对象,都能把他转化为数组。

let arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
}
Array.from(arrayLike);

39.Array.of返回参数构成的数组,如果没有参数,就返回一个空数组。


40.copyWithin()
数组实例的copyWithin方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组。


41.find方法,findIndex方法
entries keys values方法
includes 方法
ps:这几个方法写了三次了都,都是在草稿箱忘了保存了。
但是有个重点是find和findInex都会发现NaN,弥补了indexOf和lastIndexOf()的不足。


42.数组的空位
forEach(), filter(), reduce(), every() 和some()都会跳过空位。
map()会跳过空位,但会保留这个值
join()和toString()会将空位视为undefined,而undefined和null会被处理成空字符串。
es6明确指出,数组的空位会转化为undefined


43.对象的简洁表示法
在对象中,es6允许咋变量名和属性值一样时,只需写一个即可

const foo = 'bar';
const baz = {foo};
baz // {foo: "bar"}

// 等同于
const baz = {foo: foo};
function f(x, y) {
  return {x, y};
}

// 等同于

function f(x, y) {
  return {x: x, y: y};
}

f(1, 2) // Object {x: 1, y: 2}

除了属性,方法也可以简写

const o ={
    methods:funciton(){
        return 1;   
    }
}
//等同于
const o = {
    methods(){
        return 1;
    }
}

下面是一个混合的例子

let birth = '2000/01/01';

const Person = {

  name: '张三',

  //等同于birth: birth
  birth,

  // 等同于hello: function ()...
  hello() { console.log('我的名字是', this.name); }
};

43.对象的属性名表达式
使用大括号定义对象es5中定义对象只能

var people = {
    name:'Jhin',
    age:12
}

es6中定义对象的属性名表达式:

var peopKey = 'foo';
var people = {
    [peopKey]:'thiskey',
    ['a'+'bc']:'thatkey'
}

也可以适用于函数名字

let obj = {
    ['h'+'ello'](){
        return 'hi';
    }
};
obj.hello();//hi

需要注意的是属性名与简洁表示法,不能同时使用,会报错

// 报错
const foo = 'bar';
const bar = 'abc';
const baz = { [foo] };

// 正确
const foo = 'bar';
const baz = { [foo]: 'abc'};

如果属性名是个对象,默认情况下会将对象转化为字符串,这点要特别小心。


44.Object.is()
es5比较两个值是否相等,只有两个运算符,==和===,但是这两个运算符都有缺点,==号会自动转化数据类型,后者NaN不等于自身,以及+0等于-0。javascript缺乏一种运算,在所有环境中,只要是两值是一样的,他们就应该相等。
Object.is()就此诞生,它用来比较两个值是否严格相等,与严格运算符(===)的行为基本一致。
不同之处也能很简单:NaN等于NaN,并且
+0不等于-0。;


45.Object.assign()
Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)

const target = {a:1};
const source1 = {b:2};
const source2 = {c:3};
Object.assign(target,source1,source2);
target; //{a:1, b:2, c:3}

规则:
1.Object.assign方法的第一个参数是目标对象,其余的参数都是源对象。
2.如果目标对象和后面的源对象有重复属性,或者源对象与其他源对象有相同的属性,那么后面的会覆盖前面的属性。
3.如果只有一个参数,那么Object.assign会返回这个参数。
4.如果这个参数不是对象,那么Object.assign会先转化为对象然后返回。
5.由于undefined和null无法转化为对象,所以当用undefined和null做首参数的时候,就会报错。
6.如果非对象参数出现在源对象的位置,会首先把非对象参数转化为对象,如果不能转化为对象,就会跳过,这就意味这,如果undefined和null不在首参数就不会报错。
7.其他类型的值(数组,字符串,布尔)不在首参数,也不会报错,单数除了字符串会以数组的形式,拷贝入目标对象,其他值不会残生任何结果。

const v1 = 'abc';
const v2 = true;
const v3 = 10;

const obj = Object.assign({}, v1, v2, v3);
console.log(obj); // { "0": "a", "1": "b", "2": "c" }

注意点:Object.assign方法实现的是浅拷贝,也就是说,如果目标对象拷贝得到的是对象的引用。

目前有四个操作会忽略enumerable为false属性。
for—-in循环:只遍历自身的可枚举属性和继承的可枚举属性。
Object.keys():返回对象自身的所有可枚举属性。
JSON.stringify():只串行化对象自身的可枚举属性。
Object.assign:忽略enumberalbe为false属性的,只拷贝对象自身的可枚举属性。

注意for—-in会遍历继承的属性,其他三个方法都会忽略继承的属性。
尽可能不能for—in 而选择Object.keys()代替。


46.Object.getOwnPropertyDescriptors();
这个方法会返回投个对象属性的描述对象。


47.对象的扩展运算符
对象的解构赋值。

let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x // 1
y // 2
z // { a: 3, b: 4 }

1.解构赋值要求等号右边是一个对象,所以如果等号右边undefined或者null,就会报错,因为他们无法转化为对象。
2.解构赋值必须是最后一个参数,否则会报错。

对象的扩展运算符(….)用于取出参数对象的可遍历属性,拷贝到当前对象之中。

let z = {a:3,b:4};
let n = {...z};

扩展运算符还可用与合并两个对象。

let ab = {...a,...b};

猜你喜欢

转载自blog.csdn.net/weixin_42158320/article/details/80426993