es5全名為“ECMAScript 5”,是ECMAScript標準的第5版,它透過對現有JavaScript方法添加語句和原生ECMAScript物件做合併實現標準化;ES5還引入了一個語法的嚴格變種,被稱為”嚴格模式」。 es6全名為ECMAScript6,是於2015年6月正式發布的JS語言的標準;它的目標是使得JS語言可以用來編寫複雜的大型應用程序,成為企業級開發語言。
本教學操作環境:windows7系統、ECMAScript 6版、Dell G3電腦。
es全名為「ECMAScript」
ECMAScript是一種由Ecma國際(前身為歐洲電腦製造商協會,European Computer Manufacturers Association)透過ECMA-262標準化的腳本程式設計語言。這種語言在萬維網上應用廣泛,它往往被稱為JavaScript或JScript,所以它可以理解為是JavaScript的一個標準,但實際上後兩者是ECMA-262標準的實現和擴展。
ECMAScript是一種可以在宿主環境中執行計算並能操作可計算物件的基於物件的程式設計語言。 ECMAScript最先被設計成一種Web腳本語言,用來支援Web頁面的動態表現以及為基於Web的客戶機—伺服器架構提供伺服器端的運算能力。但作為一種腳本語言, ECMAScript具備同其他腳本語言一樣的性質,即「用來操縱、定制一個已存在系統所提供的功能,以及對其進行自動化」。
es5是什麼
es5全名為“ECMAScript 5”,也稱為ECMAScript2009,是ECMAScript標準的第5版,與HTML5規範進程本質類似,ES5透過對現有JavaScript方法添加語句和原生ECMAScript物件做合併實現標準化。 ES5也引入了一個語法的嚴格變種,被稱為」嚴格模式(strict mode)」。
es5的新功能:
這些是2009 年發布的新功能:
The "use strict" Directive
String.trim()
Array.isArray()
Array.forEach( )
Array.map()
#Array.filter()
给作者提供了选择一个限制性更强语言变种的方式
1. 消除不安全之处,保证安全运行
2. 提升编译效率
3. 未来发展趋势
规则:
1)变量先定义在使用
2)不允许变量重名
var a=1;
function a(){}
3)不允许使用eval
4)不允许delete
4)不允许with语句
var sMessage = "hello";
with(sMessage) {alert(toUpperCase());}
es5是什麼es6全名為ECMAScript6(ECMAScript的第6個版本),是於2015年6月正式發表的JavaScript語言的標準,正式名為ECMAScript 2015(ES2015)。它的目標是使得JavaScript語言可以用來編寫複雜的大型應用程序,成為企業級開發語言。
ES6是JavaScript語言的主要增強,允許我們編寫程式。 ES6適用於複雜的應用程式。儘管ES5和ES6在本質上有一些相似之處,但它們之間也有許多不同之處。
es5與es6的差異 ES5與ES6的比較清單如下:
比較項目 | ES5 | ES6 |
---|---|---|
定義 | ES5是ECMAScript(由ECMA International定義的商標腳本語言規範)的第五版。 | ES6是ECMAScript(ECMA International定義的商標腳本語言規範)的第六版。 |
發布 | 它在2009年推出。 | 它在2015年推出。 |
資料類型 | ES5支援原始資料類型,包括字串,數字,布林值,空值和未定義(undefined )。 | 在ES6中,對JavaScript資料型別做了一些補充。它引入了一種新的原始資料類型symbol 以支援唯一值。 |
定義變數 | 在ES5中,只能使用var 關鍵字定義變數。 | 在ES6中,有兩種定義let 和const 變數的新方法。 |
效能 | 由於ES5早於ES6,因此某些功能不存在,因此其效能比ES6低。 | 由於具有新功能和速記儲存實現,因此ES6具有比ES5更高的效能。 |
支持 | 許多社群都支持它。 | 它也有很多社群支持,但是比ES5小。 |
物件操縱 | ES5比ES6耗時。 | 由於具有解構和速度運算符,因此可以在ES6中更平穩地處理物件操縱。 |
箭頭函數 | 在ES5中,function 和return 關鍵字都用於定義函數。 | 箭頭功能是ES6中引入的新功能,透過它不需要function 關鍵字來定義函數。 |
循環 | 在ES5中,使用了for 循環來遍歷元素。 | ES6引入了for?of 循環的概念,以對可迭代物件的值執行迭代。 |
代码转换
到目前为止,还没有完全支持ES6功能的浏览器。 但是,我们可以使用转译将ES6代码转换为ES5代码。
有两个主要的编译器Babel和Traceur,用于在构建过程中将ES6代码转换为ES5代码。
扩展运算符(…)
它在ES6中引入,使合并数组和对象变得容易。
ES6常用特性:
(1)基本用法
ES6 新增了let命令,用来声明变量。类似于var,但是所声明的变量,只在let命令所在的代码块内有效。{
let a = 10; var b = 1; } a // ReferenceError: a is not defined. b // 1
(2)let在for循环中使用
var a = []; for (var i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[2](); // 10 a[6](); // 10
这个例子中,i 由 var 声明,在全局范围有效,a 的所有组员里面的 i 都是指向同一个 i,导致所有组员运行输出的都是最后一轮的 i 的值。
var a = []; for (let i = 0; i < 10; i++) { a[i] = function () { console.log(i); } } a[2](); // 2 a[6](); // 6
这个例子中, i 由 let 声明,当前的 i 只在本轮循环有效,所有每次循环的 i 其实都是一个新的变量。
Tips: 每轮循环的 i 都是重新声明的,JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的 i 时,就在上一轮循环的基础上进行计算。
(3)for循环的特别之处
在 for 循环中,设置循环变量的那部分是一个父作用域,而循环内部是一个单独的子作用域。
for (let i = 0; i < 3; i++) { let i = 'abc'; // 和上一行for循环计数器的i不在同一作用域,所以可以声明成功。类比下面要讲的function(arg){let arg;}会报错,就是作用域问题 console.log(i); } // abc // abc // abc
(4)不能重复声明
let 不允许在相同作用域内,重复声明同一个变量。
function func() { var a = 10; let a = 1; } func(); //Identifier 'a' has already been declared function func() { let a = 10; let a = 1; } func(); //Identifier 'a' has already been declared function func(arg) { let arg; } func(); //Identifier 'arg' has already been declared function func(arg) { { let arg; } } func(); //不报错
(5)不存在变量提升
var 命令会发生“变量提升”现象,即变量可以在声明之前使用,值为 undefined。 let 命令改变了语法行为,它所声明的变量一定要在声明后使用,否则会报错。
console.log(foo); //undefined var foo = 2; console.log(bar); // ReferenceError: bar is not defined let bar = 2;
(6)暂时性死区
ES6规定,如果区块中存在 let 和 const 命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前使用这些变量,就会报错。这在语法上称为 “暂时性死区”。
var tmp = 123; if(true){ tmp = 'abc'; // ReferenceError: tmp is not defined let tmp; }
Tips:“暂时性死区”的本质就是:只要一进入当前作用域,所要使用的变量就已经存在了,但不可获取(否则报错),只有等到声明的那一行代码出现,才可以获取和使用。
const命令的用法和let相似,最大不同点就是:const声明一个只读的常量。一旦声明,常量的值就不能改变。
Tips:这个不可改变的是指针,所以对于const声明的对象和数组还是可以改变的。如果真的想将对象冻结,应该使用Object.freeze方法。
ES5 只有两种声明变量的方法:var命令和function命令。
ES6 除了添加let和const命令,另外两种声明变量的方法:import命令和class命令。所以,ES6 一共有 6 种声明变量的方法。
顶层对象:在浏览器环境指的是window
对象,在 Node 指的是global
对象。ES5 之中,顶层对象的属性与全局变量是等价的。
ES6为了保持兼容性,规定:a. var 命令和 function 命令声明的全局变量,依旧是顶层对象的属性。b. let,const,class 命令声明的全局变量,不属于顶层对象的属性。
var a = 1; window.a; // 1 let b = 2; window.b; // undefined
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
(1)基本用法
// 之前,为变量赋值,可以直接指定值 let a = 1; let b = 2; let c = 3; // ES6中允许这样 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 [head, ...tail] = [1, 2, 3, 4]; head // 1 tail // [2, 3, 4] let [x, y, ...z] = ['a']; x // "a" y // undefined - 如果解构不成功,变量的值就等于 undefined。 z // []
如果解构不成功,变量的值就等于undefined
。
上面一种情况是解构不成功的;另一种情况是不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功。
let [a, [b], d] = [1, [2, 3], 4]; a // 1 b // 2 d // 4
上面两个例子,都属于不完全解构,但是可以成功。
如果等号的右边不是数组,那么将会报错。
let [foo] = 1; let [foo] = false; let [foo] = NaN; let [foo] = undefined; let [foo] = null; let [foo] = {};
(2)默认值
解构赋值允许指定默认值。例如:
let [foo = true] = []; foo;// true let [x, y = 'b'] = ['a']; x;//'a' y;//'b'
Tips:ES6 内部使用严格相等运算符(===)去判断一个位置是否有值。所以,只有当一个数组成员严格等于 undefined ,默认值才会生效。例如:
let [x = 1] = [undefined]; x // 1 let [x = 1] = [null]; x // null - 默认值就不会生效,因为null不严格等于undefined
默认值可以引用解构赋值的其他变量,但该变量必须已经声明。
let [x = 1, y = x] = [1, 2]; x;//1 y;//2 let [x = y, y = 1] = [];// ReferenceError: y is not defined -- let的暂时性死区 let [x = y, y = 1] = [1, 2];
(1)基本用法
let { bar, foo } = { foo: "aaa", bar: "bbb" }; foo // "aaa" bar // "bbb" let { baz } = { foo: "aaa", bar: "bbb" }; baz // undefined
对象的解构与数组的解构,一个重要的不同点在于: 数组元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量名与属性同名,才能取到正确的值。
如果没有与变量名对应的属性名,导致取不到值,最后等于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: foo, bar: bar } = { foo: "aaa", bar: "bbb" };
也就是说,对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。
let { foo: baz } = { foo: "aaa", bar: "bbb" }; baz // "aaa" foo // error: foo is not defined
上面代码中,foo
是匹配的模式,baz
才是变量。真正被赋值的是变量baz
,而不是模式foo
。
(2)默认值
对象的解构也可以指定默认值。默认值生效的条件是对象的属性值严格等于 undefined。
let {x, y = 5} = {x: 1}; x;// 1 y;// 5 let { message: msg = 'Something went wrong' } = {}; msg;//"Something went wrong"
Tips:如果要将一个已经声明的变量用于解构赋值,需要注意:
let x; {x} = {x:1}; //上面的代码会报错,因为JavaScript引擎会将 {x} 理解为一个代码块,从而发生语法错误。只有不将大括号写在行首,避免JavaScript将其解释为代码块,才能解决这个问题。 //正确的写法,将需要解构的部分用括号()包起来 let x; ({x} = {x:1});
字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象。
const [a, b, c, d, e] = 'hello'; a // "h" b // "e" c // "l" d // "l" e // "o"
类似数组的对象都有一个 length 属性,因此还可以对这个属性解构赋值。
let {length: len} = 'hello'; len;// 5
解构赋值时,如果等号右边是数值和布尔值,则会先转化为对象:
let {toString: s} = 123; s === Number.prototype.toString // true let {toString: s} = true; s === Boolean.prototype.toString // true
上面代码中,数值和布尔值的包装对象都有 toString 属性,因此变量 s 都可以取到值。
解构赋值的规则是:只要等号右边的值不是对象或数组,就先将其转化为对象。 由于 undefined 和 null 无法转化为对象,所以对他们进行解构赋值都会报错。
function add([x, y]){ return x + y; } add([1, 2]); // 3 [[1, 2], [3, 4]].map(([a, b]) => a + b); // [ 3, 7 ]
上面代码中,函数 add 的参数看似是一个数组,但在传入参数时,数组参数就解构为变量 x 和 y。对于函数内部代码来说,参数就是 x 和 y。
函数参数的解构也可以使用默认参数:
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 指定默认值。
(1)交换变量的值
let x = 1; let y = 2; [x, y] = [y, x]; x;// 2 y;// 1
(2)从函数返回多个值
函数只能返回一个值,如果要返回多个值,只能将它们放在数组或对象里面返回。有了解构赋值,取出这些值就非常方便。
//返回一个数组 function example() { return [1, 2, 3]; } let [a, b, c] = example(); a;//1 b;//2 c;//3 //返回一个对象 function example(){ return { foo: 1, bar: 2 } } let {foo, bar} = example(); foo;//1 bar;//2
(3)函数参数的定义
解构赋值可以方便的将一组参数与变量名对应起来。
//参数是一组有序值 function f([x, y, z]) {} f([1, 2, 3]); //参数是一组无序值 function f({x, y, z}) {} f({x: 1, y: 2, z: 3});
(4)提取JSON数据
let data = { id: 1, status: 'ok', data: [867, 5612] }; let {id, status, data:number} = data; console.log(id, status, number);// 1 "ok" [867, 5612]
(5)遍历Map解构
const map = new Map(); map.set('first','hello'); map.set('second','world'); for (let [key, value] of map) { console.log(key + ' is ' + value ); } //first is hello //second is world // 获取键名 for (let [key] of map) {} // 获取键值 for (let [,value] of map) {}
ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法。
function log(x, y) { y = y || 'World'; console.log(x, y); } log('Hello') // Hello World log('Hello', 'China') // Hello China log('Hello', '') // Hello World
上面代码检查函数log的参数y有没有赋值,如果没有,则指定默认值为World。这种写法的缺点在于,如果参数y赋值了,但是对应的布尔值为false,则该赋值不起作用。就像上面代码的最后一行,参数y等于空字符,结果被改为默认值。
为了避免这个问题,通常需要先判断一下参数y是否被赋值,如果没有,再等于默认值。
if (typeof y === 'undefined') { y = 'World'; }
ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。
function log(x, y = 'World') { console.log(x, y); } log('Hello') // Hello World log('Hello', 'China') // Hello China log('Hello', '') // Hello -- 上面说过,ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,只有当一个数组成员严格等于 undefined ,默认值才会生效。
function foo({x, y = 5}) { console.log(x, y); } foo({}) // undefined 5 foo({x: 1}) // 1 5 foo({x: 1, y: 2}) // 1 2 foo() // TypeError: Cannot read property 'x' of undefined
上面代码只使用了对象的解构赋值默认值,没有使用函数参数的默认值。只有当函数foo的参数是一个对象时,变量x和y才会通过解构赋值生成。
如果函数foo调用时没提供参数,变量x和y就不会生成,从而报错。
通过提供函数参数的默认值,就可以避免这种情况。
function foo({x, y = 5} = {}) { console.log(x, y); } foo() // undefined 5
下面是另一个解构赋值默认值的例子:
function fetch(url, { body = '', method = 'GET', headers = {} }) { console.log(method); } fetch('http://example.com', {}) // "GET" fetch('http://example.com') // 报错
上面代码中,如果函数fetch的第二个参数是一个对象,就可以为它的三个属性设置默认值。
这种写法不能省略第二个参数,如果结合函数参数的默认值,就可以省略第二个参数。这时,就出现了双重默认值。
function fetch(url, { body = '', method = 'GET', headers = {} } = {}) { console.log(method); } fetch('http://example.com') // "GET"
上面代码中,函数fetch没有第二个参数时,函数参数的默认值就会生效,然后才是解构赋值的默认值生效,变量method才会取到默认值GET。
因此,与解构赋值默认值结合使用时,切记,函数要定义默认参数,防止函数不传参时报错。
模板字符串是增强版的字符串,用反引号 (`) 标识。它可以当普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
let greeting = `\`Yo\` World!`; // `Yo` World -- 使用反斜杠转义反引号 // 多行字符串 $('#result').append(` There are <b>${basket.count}</b> items in your basket, <em>${basket.onSale}</em> are on sale! `);
Tips:
a. 如果在模板字符串中需要使用反引号,则前面需要使用反斜杠转义。
b. 如果使用模板字符串表示多行字符串,所有的空格和缩进都会被保留在输出之中。
(1) 在模板字符串中嵌入变量,需要将变量名写在 ${} 之中。
(2) 大括号内部可以放入任意的JavaScript表达式,可以进行运算,以及引用对象属性。
(3) 模板字符串之中还能调用函数。
(4) 如果大括号中的值不是字符串,将按照一般的规则转为字符串。如: 如果大括号中是一个对象,将默认调用对象的 toString 方法。
(5) 如果模板字符串中的变量没有声明将会报错。
(6) 如果大括号内部是一个字符串,将原样输出。
//普通字符串 `In JavaScript '\n' is a line-feed.`; `Hello ${'World'}`; // "Hello World" //字符串中嵌入变量 let name = "Bob", time = "today"; console.log(`Hello ${name}, how are you ${time}?`); // Hello Bob, how are you today? //字符串中嵌入 JavaScript 表达式 `${x} + ${y} = ${x + y}`;// "1 + 2 = 3" `${x} + ${y * 2} = ${x + y * 2}`;// "1 + 4 = 5" let obj = {x: 1, y: 2}; `${obj.x + obj.y}`;// "3" // 字符串中嵌入函数 function fn(){ return "hello world" } `foo ${fn()} bar`;// "foo hello world bar" // 字符串中嵌入对象 `${obj}`;// "[object Object]" // 字符串中嵌入未声明的变量 `Hello, ${place}`;// ReferenceError: place is not defined
ES6 允许使用 “箭头” (=>)定义函数。
var f = v => v; //等同于 var f = function (v) { return v; }
=> 前面的部分是函数的参数,=> 后面的部分是函数的代码块。
(1)如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。
var f = () => 5; //等同于 var f = function (v) { return 5 }; var sum = (num1, num2) => num1 + num2; //等同于 var sum = function(num1, num2){ return num1 + num2; }
(2)如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用 return 语句返回。
var sum = (num1,num2) => { return num1 + num2; }
(3)由于大括号会被解释为代码块,所以如果箭头函数直接返回一个对象,就必须在对象外面加上括号,否则会报错。
// 报错 let getTempItem = id => { id: id, name: "Temp" }; // 不报错 let getTempItem = id => ({ id: id, name: "Temp" }); getTempItem(1);//{id: 1, name: "Temp"} let getTempItem = id => { return { id: id, name: "Temp" }; } getTempItem(1);// {id: 1, name: "Temp"}
(4)箭头函数内部,可以嵌套箭头函数
function insert (value) { return {into: function (array){ return {after: function (afterValue) { array.splice(array.indexOf(ahterValue)+1, 0, value); return array; } } } } } insert(2).into([1, 3]).after(1); //[1, 2, 3] //使用箭头函数改写 let insert = (value) => ({into: (array) => ({after: (afterValue) => { array.splice(array.indexOf(afterValue)+1, 0, value); return array; } }) });
const full = ({ first, last }) => first + ' ' + last; full({first: 1, last: 2});// "1 2" // 等同于 function full(person) { return person.first + ' ' + person.last; } full({first: 1, last: 2});// "1 2"
const numbers = (...nums) => nums; numbers(1,2,3,4);// [1, 2, 3, 4] const headAndTail = (head, ...tail) => [head, tail]; headAndTail(1,2,3,4);// [1,[2,3,4]]
(1)简化代码
const isEven = n => n%2 == 0; isEven(3);// false isEven(4);// true
(2)简化回调函数
[1, 2, 3].map( x => x*x ); //[1, 4, 9] [3,2,5].sort((a, b) => a - b); // [2, 3, 5]
ES5中 this 的指向是可变的,但是箭头函数中 this 指向固定化。
var handler = { id: '123', init: function () { document.addEventListener('click', event => this.doSomethisng(event.type), false); }, doSomethisng: function (type) { console.log('Handling ' + type + ' for ' + this.id); } }; handler.init(); //Handling click for 123 -- this指向handle -- this是属于函数的一个对象,谁调用指向谁(es5中)
this 指向的固定化,并不是因为箭头函数内部有绑定 this 的机制,实际原因是箭头函数内部根本没有自己的this,导致内部的this 就是外层代码块的 this 。
箭头函数的this是比较难理解的,但只要能想象到他是如何从ES5转化来的,就可以完全理解它的作用对象。 箭头函数转成 ES5 的代码如下:
// ES6 function foo() { setTimeout(() => { console.log('id:', this.id); }, 100); } // ES5 function foo() { var _this = this; setTimeout(function () { console.log('id:', _this.id); }, 100); }
Tips:
(1) 函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象。
(2) 不可以当构造函数,即不可以使用 new 命令,因为它没有 this,否则会抛出一个错误。
(3) 箭头函数没有自己的 this,所以不能使用 call()、apply()、bind() 这些方法去改变 this 指向。
(4) 不可以使用arguments 对象,该对象在函数体内不存在。如果要用,可以使用rest参数代替。
(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>]
该运算符主要用于函数调用。扩展运算符与正常的函数参数可以结合使用,非常灵活。function push(array, ...items) {
array.push(...items); } function add(x, y) { return x + y; } const numbers = [4, 38]; add(...numbers) // 42 function f(v, w, x, y, z) { } const args = [0, 1]; f(-1, ...args, 2, ...[3]);
如果扩展运算符后面是一个空数组,则不产生任何效果。 [...[], 1] // [1]
注意,扩展运算符如果放在括号中,JavaScript 引擎就会认为这是函数调用,否则就会报错。(...[1,2])
// Uncaught SyntaxError: Unexpected number console.log((...[1,2])) // Uncaught SyntaxError: Unexpected number console.log(...[1,2]) // 不会报错
由于扩展运算符可以展开数组,所以不再需要apply
方法,将数组转为函数的参数了。
// ES5 的写法 function f(x, y, z) { // ... } var args = [0, 1, 2]; f.apply(null, args); // ES6的写法 function f(x, y, z) { // ... } let args = [0, 1, 2]; f(...args);
3、扩展运算符引用
(1)复制数组
数组是复合的数据类型,直接复制的话,只是复制了指向底层数据结构的指针(浅拷贝),而不是克隆一个全新的数组。
如何实现深拷贝呢?下面看看ES5和ES6的实现:
// ES5 const a1 = [1, 2]; const a2 = a1.concat(); // ES6 const a1 = [1, 2]; // 写法一 const a2 = [...a1]; // 写法二 const [...a2] = a1; a2[0] = 2; a1 // [1, 2]
(2)合并数组
const arr1 = ['a', 'b']; const arr2 = ['c']; const arr3 = ['d', 'e']; // ES5 的合并数组 arr1.concat(arr2, arr3); // [ 'a', 'b', 'c', 'd', 'e' ] // ES6 的合并数组 [...arr1, ...arr2, ...arr3] // [ 'a', 'b', 'c', 'd', 'e' ]
不过,这两种方法都是浅拷贝,使用的时候需要注意。如果修改了原数组的成员,会同步反映到新数组。
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
(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 // []
注意:如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。
const [...butLast, last] = [1, 2, 3, 4, 5]; // 报错 const [first, ...middle, last] = [1, 2, 3, 4, 5]; // 报错
(4)字符串
扩展运算符可以将字符串和函数参数arguments转成真正的数组
[...'hello'] // [ "h", "e", "l", "l", "o" ] function (){ let arr = [...arguments]; }
ES6 允许直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。
const foo = 'bar'; const baz = {foo}; baz // {foo: "bar"} // 等同于 const baz = {foo: foo};
上面代码表明,ES6 允许在对象之中,直接写变量。这时,属性名为变量名, 属性值为变量的值
属性简写:
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 = { method() { return "Hello!"; } }; // 等同于 const o = { method: function() { return "Hello!"; } };
CommonJS 模块输出一组变量,就非常合适使用简洁写法。
let ms = {}; function getItem (key) { return key in ms ? ms[key] : null; } function setItem (key, value) { ms[key] = value; } function clear () { ms = {}; } module.exports = { getItem, setItem, clear }; // 等同于 module.exports = { getItem: getItem, setItem: setItem, clear: clear };
JavaScript 定义对象的属性,有两种方法。
// 方法一 obj.foo = true; // 方法二 obj['a' + 'bc'] = 123;
上面代码的方法一是直接用标识符作为属性名,方法二是用表达式作为属性名,这时要将表达式放在方括号之内。
但是,如果使用字面量方式定义对象(使用大括号),在 ES5 中只能使用方法一(标识符)定义属性。
var obj = { foo: true, abc: 123 };
ES6 允许字面量定义对象时,用方法二(表达式)作为对象的属性名,即把表达式放在方括号内。
let propKey = 'foo'; let obj = { [propKey]: true, ['a' + 'bc']: 123 };
表达式还可以用于定义方法名。比如我们熟悉的Vuex定义Mutation就是推荐这种方式:
// mutation-types.js export const SOME_MUTATION = 'SOME_MUTATION' // store.js import Vuex from 'vuex' import { SOME_MUTATION } from './mutation-types' const store = new Vuex.Store({ state: { ... }, mutations: { // 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名 [SOME_MUTATION] (state) { // mutate state } } })
var test = { 'attr0' : 'attr1', 'attr1' : 'attr2', 'attr2' : 'attr3', 'attr3' : 'I'm here', } // 输出: I'm here , 方括号可以接受任何JS语句,最后都被转化为字符串。利用这个字符串在key-value集合中寻址。 console.log(test[test[test[test['attr0']]]]);
扩展运算符(展开语法):扩展运算符可以将数组或者对象转为用逗号分隔的参数序列
let ary = [1, 2, 3]; ...ary // 1, 2, 3console.log(...ary); // 1 2 3,相当于下面的代码console.log(1,2,3);
扩展运算符可以应用于合并数组
// 方法一 let ary1 = [1, 2, 3]; let ary2 = [3, 4, 5]; let ary3 = [...ary1, ...ary2];// 方法二 ary1.push(...ary2);
构造函数方法:Array.from()
将伪数组或可遍历对象转换为真正的数组
//定义一个集合let arrayLike = {'0': 'a','1': 'b','2': 'c', length: 3}; //转成数组let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
方法还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组
let arrayLike = { "0": 1,"1": 2,"length": 2} let newAry = Array.from(arrayLike, item => item *2)//[2,4]注意:如果是对象,那么属性需要写对应的索引
实例方法:find()
用于找出第一个符合条件的数组成员,如果没有找到返回undefined
let ary = [{ id: 1, name: '张三'}, { id: 2, name: '李四'}]; let target = ary.find((item, index) => item.id == 2);//找数组里面符合条件的值,当数组中元素id等于2的查找出来,注意,只会匹配第一个
实例方法:findIndex()
用于找出第一个符合条件的数组成员的位置,如果没有找到返回-1
let ary = [1, 5, 10, 15]; let index = ary.findIndex((value, index) => value > 9); console.log(index); // 2
实例方法:includes()
判断某个数组是否包含给定的值,返回布尔值。
[1, 2, 3].includes(2) // true [1, 2, 3].includes(4) // false
arr.splice(index,len,[item])
表示从数组中删除或者添加元素,返回新数组保存所有删除的元素
index表示要操作的索引位置,len表示要删除的个数,如果len的值为0表示不删除,item表示要添加的元素,可以是多个
let ary = [10, 20, 50]; // 把20给删除 // ary.splice(1, 1); // console.log(ary); // 把20删除,替换成30 // ary.splice(1, 1, 30); // console.log(ary); // 在20前面加上15 // ary.splice(1, 0, 15); // console.log(ary); // 在50前面加上30,40ary.splice(2, 0, 30, 40); console.log(ary);
arr.filter()
返回数组,包含了符合条件的所有元素。如果没有符合条件的元素则返回空数组
// 返回数组,包含了符合条件的所有元素。如果没有符合条件的元素则返回空数组 // let result = ary.filter(item => item > 150); // console.log(result)
String 的扩展方法
实例方法:repeat()
repeat方法表示将原字符串重复n次,返回一个新字符串
'x'.repeat(3) // "xxx" 'hello'.repeat(2) // "hellohello"
Set 数据结构
ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set本身是一个构造函数,用来生成 Set 数据结构
const s = new Set();
Set函数可以接受一个数组作为参数,用来初始化。
const set = new Set([1, 2, 3, 4, 4]);//{1, 2, 3, 4}
实例方法
add(value):添加某个值,返回 Set 结构本身
delete(value):删除某个值,返回一个布尔值,表示删除是否成功
has(value):返回一个布尔值,表示该值是否为 Set 的成员
clear():清除所有成员,没有返回值
const s = new Set(); s.add(1).add(2).add(3); // 向 set 结构中添加值 s.delete(2) // 删除 set 结构中的2值 s.has(1) // 表示 set 结构中是否有1这个值 返回布尔值 s.clear() // 清除 set 结构中的所有值//注意:删除的是元素的值,不是代表的索引
遍历
Set 结构的实例与数组一样,也拥有forEach方法,用于对每个成员执行某种操作,没有返回值。
s.forEach(value => console.log(value))
【推荐学习:javascript视频教程】
以上是什麼是es5和es6的詳細內容。更多資訊請關注PHP中文網其他相關文章!