es6总结第一篇

一.let与const命令

1.Babel 转码器

(1)babel-node
@babel/node模块的babel-node命令,提供一个支持 ES6 的 REPL 环境。
【注】Node.js给我们提供一个交互式运行环境——REPL,在这个环境中我们可以做一些简单的应用程序的测试或调试。进入命令行窗口,输入”node”命令并按下回车键,即可进入REPL运行环境。
(2)babel/register 模块
@babel/register只会对require命令加载的文件转码,而不会对当前文件转码。

2.shims 与pollfill

shims 垫片
在js中:
shim一般指一些做兼容性的库,用来弥补旧浏览器对新特性支持的不足。都会预先加载,强调新旧浏览器使用同一套代码。
pollfill也会对浏览器的不足做补充,但一般都会用语句来判断此浏览器是否支持此特性,然后通过动态引入script标签的方法来加载。

3.let

let声明的变量只在它所在的代码块有效。
当 ***设置循环变量***使用let声明时,
for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。
【注】在if/for语句var定义的变量会提升(在严格模式下一样)

	console.log(a);
	if (false/true) {
	var a = 1;
	}
	
	//undefined
	
	console.log(i);
	
	for (var i = 1; i < 5; i++) {
	}

(1)let与var的区别:
A.let不存在变量提升 ,它所声明的变量一定要在声明后使用,否则报错;
B.暂时性死区,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
C.不允许重复声明
(2)函数声明在es5与es6中的不同
es5中:
存在变量提升;
es6中:
允许在块级作用域内声明函数。
函数声明类似于var,即会提升到全局作用域或函数作用域的头部。
同时,函数声明还会提升到所在的块级作用域的头部。

考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句。

// 块级作用域内部的函数声明语句,建议不要使用
{
  let a = 'secret';
  function f() {
    return a;
  }
}

// 块级作用域内部,优先使用函数表达式
{
  let a = 'secret';
  let f = function () {
    return a;
  };
}

4.const

const的作用域与let命令相同:只在声明所在的块级作用域内有效。
const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。
const声明的常量,也与let一样不可重复声明。
对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。

const foo = {};

// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123

// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only

彻底冻结对象的属性与方法:
Object.freeze()兼容在ie9以及ie9以上;

var constantize = (obj) => {
  Object.freeze(obj);
  Object.keys(obj).forEach( (key, i) => {
    if ( typeof obj[key] === 'object' ) {
      constantize( obj[key] );
    }
  });
};

ES6 为了改变这一点,一方面规定,为了保持兼容性,var命令和function命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。也就是说,从 ES6 开始,全局变量将逐步与顶层对象的属性脱钩。

二.es6解构赋值

1.数组解构赋值—默认值

解构赋值允许指定默认值

let [foo = true] = [];
foo // true
//当没有对参数b结构赋值时,默认值生效。
let [x, y = 'b'] = ['a']; // x='a', y='b'
//当对参数b解构赋值时,只有当一个数组成员严格等于undefined,默认值才会生效。
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'

注意,ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,只有当一个数组成员严格等于undefined,默认值才会生效。
如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值。

function f() {
  console.log('aaa');
}

let [x = f()] = [1];

上面代码中,因为x能取到值,所以函数f根本不会执行。

2.对象解构赋值

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

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 { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"
foo // error: foo is not defined

上面代码中,foo是匹配的模式,baz才是变量。真正被赋值的是变量baz,而不是模式foo。
与数组一样,解构也可以用于嵌套结构的对象。

let obj = {
  p: [
    'Hello',
    { y: 'World' }
  ]
};

let { p: [x, { y }] } = obj;
x // "Hello"
y // "World"

注意,这时p是模式,不是变量,因此不会被赋值。如果p也要作为变量赋值,可以写成下面这样。

let obj = {
  p: [
    'Hello',
    { y: 'World' }
  ]
};

let { p, p: [x, { y }] } = obj;
x // "Hello"
y // "World"
p // ["Hello", {y: "World"}]

默认值生效的条件是,对象的属性值严格等于undefined。
对象解构赋值中默认值:

var {x = 3} = {x: undefined};
x // 3

var {x = 3} = {x: null};
x // null

上面代码中,属性x等于null,因为null与undefined不严格相等,所以是个有效的赋值,导致默认值3不会生效。

将整个解构赋值语句,放在一个圆括号里面:
如果要将一个已经声明的变量用于解构赋值,必须非常小心。

// 错误的写法

let x;
{x} = {x: 1};
// SyntaxError: syntax error

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

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

3.数值和布尔值的解构赋值

解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。

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

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

解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。

4.函数参数的解构赋值

函数参数的解构使用默认值:

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]

上面代码中,函数move的参数是一个对象,通过对这个对象进行解构,得到变量x和y的值。如果解构失败,x和y等于默认值。

注意,下面的写法会得到不一样的结果。

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就会触发函数参数的默认值。

[1, undefined, 3].map((x = 'yes') => x);
// [ 1, 'yes', 3 ]

参数变量是默认声明的,所以不能用let或const再次声明。

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

上面代码中,参数变量x是默认声明的,在函数体中,不能用let或const再次声明,否则会报错。

三.字符串的扩展

1.模板编译

(1)正则表达式[\s\S]+?

?表bai示du放在其他字符后面表示前面的字符可以有,也可以没有,或者放在、+后面表示匹配尽可能少的字符。*
例如:字符串fooooo,正则fo和fo+会匹配整个字符串,
fo+? 匹配fo,
fo
? 匹配f

元字符(Metacharacter)是拥有特殊含义的字符:
\s 查找空白字符。
\S 查找非空白字符。
\w 查找单词字符。
\W 查找非单词字符。

//把 "Doe, John" 转换为 "John Doe" 的形式:
var str = "Doe, John";
str.replace(/(\w+)\s*, \s*(\w+)/, "$2 $1");
 
说明:$1,$2上就是按顺序对应小括号里面的小正则 捕获到的内容。  

修饰符:
/i 执行对大小写不敏感的匹配。
参考regexp:https://www.runoob.com/jsref/jsref-obj-regexp.html

(2)eval()

eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码。

eval(string)
(3)模板编译compile函数封装
let template = `
            <ul>
            <% for(let i=0; i < data.supplies.length; i++) { %>
                <li><%= data.supplies[i] %></li>
            <% } %>
            </ul>
            `;
        function compile(template) {
            const evalExpr = /<%=(.+?)%>/g;
            const expr = /<%([\s\S]+?)%>/g;

            template = template
                .replace(evalExpr, '`); \n  echo( $1 ); \n  echo(`')
                .replace(expr, '`); \n $1 \n  echo(`');

            template = 'echo(`' + template + '`);';

            let script =
                `(function parse(data){
                    let output = "";

                    function echo(html){
                    output += html;
                    }

                ${ template}

                return output;
             })`;

            return script;
        }
        let parse = eval(compile(template));
        var div=document.getElementsByTagName('div')[0];
        div.innerHTML = parse({ supplies: [ "broom", "mop", "cleaner" ] });

四.函数的扩展

(1)作用域

下面是一个更复杂的例子。

var x = 1;
//函数的参数可以形成一个单独的作用域。
function foo(x, y = function() { x = 2; }) {
  var x = 3;
  y();
  console.log(x);
}

foo() // 3
x // 1

上面代码中,函数foo的参数形成一个单独作用域。这个作用域里面,首先声明了变量x,然后声明了变量y,y的默认值是一个匿名函数。这个匿名函数内部的变量x,指向同一个作用域的第一个参数x。函数foo内部又声明了一个内部变量x,该变量与第一个参数x由于不是同一个作用域,所以不是同一个变量,因此执行y后,内部变量x和外部全局变量x的值都没变。

如果将var x = 3的var去除,函数foo的内部变量x就指向第一个参数x,与匿名函数内部的x是一致的,所以最后输出的就是2,而外层的全局变量x依然不受影响。

var x = 1;
function foo(x, y = function() { x = 2; }) {
  x = 3;
  y();
  console.log(x);
}

foo() // 2
x // 1

(2)rest 参数—形式(…变量名)

ES6 引入 rest 参数(形式为…变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
下面是一个 rest 参数代替arguments变量的例子

//arguments是类数组,arguments的原型上添加slice方法,并调用实例的slice方法,转为真数组,参照Array.slice源码
function sortNumbers(a,b,c,d,e) {
	  return Array.prototype.slice.call(arguments).sort();
	};  
	var arr=sortNumbers(3,1,2,7,5);
	console.log(arr);


// rest参数的写法
const sortNumbers = (...numbers) => numbers.sort();

arguments对象不是数组,而是一个类似数组的对象。所以为了使用数组的方法,必须使用Array.prototype.slice.call先将其转为数组。rest 参数就不存在这个问题,它就是一个真正的数组,数组特有的方法都可以使用。下面是一个利用 rest 参数改写数组push方法的例子。

(3)箭头函数

ES6 允许使用“箭头”(=>)定义函数。

var f = v => v;

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

如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。

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

var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
  return num1 + num2;
};

参照阮一峰博文
由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。

// 报错
let getTempItem = id => { id: id, name: "Temp" };

// 不报错
let getTempItem = id => ({ id: id, name: "Temp" });

如果箭头函数只有一行语句,且不需要返回值,可以采用下面的写法,就不用写大括号了。

//函数前面加void
let fn = () => void doesNotReturn();

箭头函数可以与变量解构结合使用。

const full = ({ first, last }) => first + ' ' + last;

// 等同于
function full(person) {
  return person.first + ' ' + person.last;
}

箭头函数有几个使用注意点。

(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象,即箭头函数的this指向父级作用域。
(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替(箭头函数中的arguments是父级函数的arguments)。
(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
箭头函数优点:
箭头函数可以让this指向固定化,这种特性很有利于封装回调函数

var handler = {
  id: '123456',

  init: function() {
    document.addEventListener('click',
      event => this.doSomething(event.type), false);
  },

  doSomething: function(type) {
    console.log('Handling ' + type  + ' for ' + this.id);
  }
};

上面代码的init方法中,使用了箭头函数,这导致这个箭头函数里面的this,总是指向handler对象。否则,回调函数运行时,this.doSomething这一行会报错,因为此时this指向document对象。

**this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。**正是因为它没有this,所以也就不能用作构造函数。
除了this,以下三个变量在箭头函数之中也是不存在的,指向外层函数的对应变量:arguments、super、new.target。

function foo() {
  setTimeout(() => {
    console.log('args:', arguments);
  }, 100);
}

foo(2, 4, 6, 8)
// args: [2, 4, 6, 8]

上面代码中,箭头函数内部的变量arguments,其实是函数foo的arguments变量。

另外,由于箭头函数没有自己的this,所以当然也就不能用call()、apply()、bind()这些方法去改变this的指向。

(function() {
  return [
    (() => this.x).bind({ x: 'inner' })()
  ];
}).call({ x: 'outer' });
// ['outer']

上面代码中,箭头函数没有自己的this,所以bind方法无效,内部的this指向外部的this。
箭头函数不适合场景:
第一个场合是定义对象的方法,且该方法内部包括this。

const cat = {
  lives: 9,
  jumps: () => {
    this.lives--;
  }
}

上面代码中,cat.jumps()方法是一个箭头函数,这是错误的。调用cat.jumps()时,如果是普通函数,该方法内部的this指向cat;如果写成上面那样的箭头函数,使得this指向全局对象,因此不会得到预期结果。这是因为对象不构成单独的作用域,导致jumps箭头函数定义时的作用域就是全局作用域。

(4)尾调用

尾调用指某个函数的最后一步是调用另一个函数。
注意,目前只有 Safari 浏览器支持尾调用优化,Chrome 和 Firefox 都不支持。
所有的调用帧,就形成一个“调用栈”(call stack)。

(5)尾递归

阶乘的非尾递归:

function factorial(n) {
  if (n === 1) return 1;
  return n * factorial(n - 1);
}

factorial(5) // 120

上面代码是一个阶乘函数,计算n的阶乘,最多需要保存n个调用记录,复杂度 O(n)
如果改写成尾递归,只保留一个调用记录,复杂度 O(1) 。

function factorial(n, total) {
  if (n === 1) return total;
  return factorial(n - 1, n * total);
}

factorial(5, 1) // 120

在es6中使用有效(尾递归优化只在严格模式下生效)。

(6)柯里化(currying)

函数式编程有一个概念,叫做柯里化(currying),意思是将多参数的函数转换成单参数的形式。

function currying(fn, n) {
  return function (m) {    //m=5,factorial函数
    return fn.call(this, m, n);  //执行tailFactorial
  };
}

function tailFactorial(n, total) {
  if (n === 1) return total;
  return tailFactorial(n - 1, n * total);
}
//等价于currying(tailFactorial, 1)(5);
const factorial = currying(tailFactorial, 1);

factorial(5) // 120

在正常模式下,函数内部有两个变量,可以跟踪函数的调用栈。
func.arguments:返回调用时函数的参数。
func.caller:返回调用当前函数的那个函数。

严格模式禁用这两个变量。

(6.1)柯里化实例

只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。
作用:1. 参数复用;2. 提前返回;3. 延迟计算/运行。
1.参数复用:

// 初步封装
var currying = function(fn) {  //currying函数的定义。
			// args 获取第一个方法内的全部参数
			var args = Array.prototype.slice.call(arguments, 1)
			return function() {  //返回值是一个函数为getWife
				// 将后面方法里的全部参数和args进行合并
				var newArgs = args.concat(Array.prototype.slice.call(arguments))
				// 把合并后的参数通过apply作为fn的参数并执行
				 //fn.apply(this, newArgs)
				 return  fn.apply(this, newArgs)   //修改fn函数的参数为newArgs数组中的元素。
			}
		}

		var getWife = currying(function() {   //currying柯里化函数的调用,currying函数返回值是一个函数为getWife。
			var allWife = [].slice.call(arguments);
			// allwife 就是所有的老婆的,包括暗渡陈仓进来的老婆
			console.log(allWife.join(","));
			return '1';
		}, "合法老婆");

		// 获得其他6个老婆
		var r1=getWife("大老婆","小老婆","俏老婆","刁蛮老婆","乖老婆","送上门老婆");
		console.log(r1);
		// 换一批老婆
		getWife("超越韦小宝的老婆");

简写:

		var currying=function(fn){
					var args=Array.prototype.slice.call(arguments,1); //wife
					return function(){  //getWife函数
						var argument=args.concat(Array.prototype.slice.call(arguments)); //参数拼接为wife,wife2,wife3
						fn.apply(this,argument);
					}
				}
		
		var getWife=currying(function(){
			var arg=Array.prototype.slice.call(arguments);
			console.log(arg);
		},'wife')
	getWife('wife2','wife3')

2.提前返回
不必每次都进行if…else if的判断:

var addEvent = (function(){
    if (window.addEventListener) {
        return function(el, sType, fn, capture) {
            el.addEventListener(sType, function(e) {
                fn.call(el, e);
            }, (capture));
        };
    } else if (window.attachEvent) {
        return function(el, sType, fn, capture) {
            el.attachEvent("on" + sType, function(e) {
                fn.call(el, e);
            });
        };
    }
})();

3.延迟计算

var curryWeight = function(fn) {
    var _fishWeight = [];
    return function() {
        if (arguments.length === 0) {
            return fn.apply(null, _fishWeight);
        } else {
            _fishWeight = _fishWeight.concat([].slice.call(arguments));
        }
    }
};
var fishWeight = 0;
var addWeight = curryWeight(function() {
    var i=0; len = arguments.length;
    for (i; i<len; i+=1) {
        fishWeight += arguments[i];
    }
});

addWeight(2.3);
addWeight(6.5);
addWeight(1.2);
addWeight(2.5);
addWeight();    //  这里才计算

console.log(fishWeight);    // 12.5

柯里化博文内容地址:https://www.zhangxinxu.com/wordpress/2013/02/js-currying/

(6.2)柯里化习题

柯里化博文内容地址:https://www.jianshu.com/p/2975c25e4d71

		// 实现一个add方法,使计算结果能够满足如下预期:
		//	add(1)(2)(3) = 6;
		//	add(1, 2, 3)(4) = 10;
		//	add(1)(2)(3)(4)(5) = 15;

		function add() {
			// 第一次执行时,定义一个数组专门用来存储所有的参数
			var _args = Array.prototype.slice.call(arguments);

			// 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
			var _adder = function() {
				_args.push(...arguments);  
				return _adder;    //递归调用
			};

			// 利用toString隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
			//	toString() 方法可把一个逻辑值转换为字符串,并返回结果。
			_adder.toString = function () {
				return _args.reduce(function (a, b) {
					return a + b;
				});
			}
			return _adder;
		}
		
		console.log(add(1)(2)(3))         // 6
		console.log(add(1, 2, 3)(4))     // 10
		console.log(add(1)(2)(3)(4)(5))  // 15
		console.log(add(2, 6)(1))  		// 9

(7)arguments.callee

callee 是 arguments 对象的一个属性。它可以用于引用该函数的函数体内当前正在执行的函数。这在函数的名称是未知时很有用,例如在没有名称的函数表达式 (也称为“匿名函数”)内。

function create() {
   return function(n) {
      if (n <= 1)
         return 1;
      return n * arguments.callee(n - 1);
   };
}

var result = create()(5); // returns 120 (5 * 4 * 3 * 2 * 1)

五.数组的扩展

(1)扩展运算符

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

console.log(...[1, 2, 3])
// 1 2 3

console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5

[...document.querySelectorAll('div')]  //函数的调用
// [<div>, <div>, <div>]

Vue中对象展开运算符我们可以这样写:

state.obj = { ...state.obj, newProp: 123 }
//计算属性对象展开运算符
computed: {
  localComputed () { /* ... */ },
  // 使用对象展开运算符将此对象混入到外部对象中
  ...mapState({
    // ...
  })
}

注意,只有函数调用时,扩展运算符才可以放在圆括号中,否则会报错。扩展运算符所在的括号应该是函数调用。

(...[1, 2])
// Uncaught SyntaxError: Unexpected number

console.log((...[1, 2]))
// Uncaught SyntaxError: Unexpected number

console.log(...[1, 2])
//扩展运算符(spread)---将一个数组转为用逗号分隔的参数序列
let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]

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

如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。

const [...butLast, last] = [1, 2, 3, 4, 5];
// 报错

const [first, ...middle, last] = [1, 2, 3, 4, 5];
// 报错

对于那些没有部署 Iterator 接口的类似数组的对象,扩展运算符就无法将其转为真正的数组。

let arrayLike = {
  '0': 'a',
  '1': 'b',
  '2': 'c',
  length: 3
};

// TypeError: Cannot spread non-iterable object.
let arr = [...arrayLike];
//,可以改为使用Array.from方法将arrayLike转为真正的数组。只要是部署了 Iterator 接口的数据结构,Array.from都能将其转为数组。

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

function add(...values) {  //函数的定义
  let sum = 0;

  for (var val of values) {
    sum += val;
  }

  return sum;
}

add(2, 5, 3) // 10

(2)Array.from()

对于还没有部署该方法的浏览器,可以用Array.prototype.slice方法替代。

const toArray = (() =>
  Array.from ? Array.from : obj => [].slice.call(obj)
)();

Array.from还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组。

Array.from(arrayLike, x => x * x);
// 等同于
Array.from(arrayLike).map(x => x * x);

Array.from([1, 2, 3], (x) => x * x)
// [1, 4, 9]

只要是部署了 Iterator 接口的数据结构,Array.from都能将其转为数组。

(3)扩展运算符与Array.from()区别

扩展运算符(…)可以将某些数据结构转为数组。

// arguments对象
function foo() {
  const args = [...arguments];
}

// NodeList对象
[...document.querySelectorAll('div')]

**扩展运算符背后调用的是遍历器接口(Symbol.iterator),如果一个对象没有部署这个接口,就无法转换。**Array.from方法还支持类似数组的对象。所谓类似数组的对象,本质特征只有一点,即必须有length属性。因此,任何有length属性的对象,都可以通过Array.from方法转为数组,而此时扩展运算符就无法转换。

Array.from({ length: 3 });
// [ undefined, undefined, undefined ]**

(4)扩展运算符的应用

1.复制数组

const a1 = [1, 2];
const a2 = a1;

a2[0] = 2;
a1 // [2, 2]

上面代码中,a2并不是a1的克隆,而是指向同一份数据的另一个指针。修改a2,会直接导致a1的变化。
扩展运算符提供了复制数组的简便写法。

const a1 = [1, 2];
// 写法一
const a2 = [...a1];
// 写法二
const [...a2] = a1;

上面的两种写法,a2都是a1的克隆。
2.合并数组

const a1 = [{ foo: 1 }];
const a2 = [{ bar: 2 }];

const a3 = a1.concat(a2);
const a4 = [...a1, ...a2];

a3[0] === a1[0] // true
a4[0] === a1[0] // true

上面代码中,a3和a4是用两种不同方法合并而成的新数组,但是它们的成员都是对原数组成员的引用,这就是浅拷贝。如果修改了引用指向的值,会同步反映到新数组。
3.
与解构赋值结合

const [first, ...rest] = [1, 2, 3, 4, 5];
first // 1
rest  // [2, 3, 4, 5]

const [first, ...rest] = [];
first // undefined
rest  // []

const [first, ...rest] = ["foo"];
first  // "foo"
rest   // []

4.字符串
扩展运算符还可以将字符串转为真正的数组。
字符串实现了 Iterator 接口。(即System.Iterator遍历器)

[…‘hello’]
// [ “h”, “e”, “l”, “l”, “o” ]

(5)Array.of()

Array.of方法用于将一组值,转换为数组。

Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1

Array.of方法可以用下面的代码模拟实现。

function ArrayOf(){
  return [].slice.call(arguments);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值