使用var的问题:
// 1. 使用var时,会存在声明提升的问题 console.log(test); var test = 'test'; // 这里会把声明部分进行提升,将会输出test // 2. 变量覆盖问题 var test = 'test1'; var test = 'test2'; console.log(test); // 此处重复声明test变量,将会替换掉test变量的值 // 3. 块级作用域的问题 for (true){ var test = 'test'; } for (var i = 0; i < 2; i++){ } console.log(test); // test console.log(i); // 2
let对上述问题的改正:
// 1. 不会声明提升 console.log(test); let test = 'test'; // 这里会报错,不会提前声明 // 2. 不可重复声明 let test = 'test1'; let test = 'test2'; console.log(test); // 此处报错,不能重复声明已经存在的变量 // 3. 修复块级作用域,let声明只在所在的作用域有效 for (true){ let test = 'test'; } for (let i = 0; i < 2; i++){ } console.log(test); console.log(i); // 此时输出将会输出test is not defined // 4. 使用let声明的变量由暂时性死区的特性 // ES6 规定,如果区块中存在 let 和 const 命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”,也就是说使用let声明的变量都是先声明再使用 ,不存在变量提升问题。 var num = 10; if (true) { console.log(num); let num; } // 报错原因:在块作用域内,let声明的变量被提升,但变量只是创建被提升,初始化并没有被提升(初始化就是给变量先赋值成undefined),在初始化之前使用变量,就会形成一个暂时性死区。
for循环计数器适合用let:
for (var i = 0; i < 10; i++) { setTimeout(function(){ console.log(i); }) } // 输出十个 10 for (let j = 0; j < 10; j++) { setTimeout(function(){ console.log(j); }) } // 输出 0123456789 /* 变量 i 是用 var 声明的,在全局范围内有效,所以全局中只有一个变量 i, 每次循环时,setTimeout 定时器里面的 i 指的是全局变量 i ,而循环里的十个 setTimeout 是在循环结束后才执行,所以此时的 i 都是 10。 变量 j 是用 let 声明的,当前的 j 只在本轮循环中有效,每次循环的 j 其实都是一个新的变量,所以 setTimeout 定时器里面的 j 其实是不同的变量,即最后输出 12345。(若每次循环的变量 j 都是重新声明的,如何知道前一个循环的值?这是因为 JavaScript 引擎内部会记住前一个循环的值)。 */
面试题:
var arr = []; for (var i = 0; i < 2; i++) { arr[i] = function () { console.log(i); } } arr[0](); //2 arr[1](); //2 // 由于var声明的i并没有自己的块级作用域,所以等for循环结束之后,函数中的console.log(i)实际上调用的是循环之后的i值2 let arr = []; for (let i = 0; i < 2; i++) { arr[i] = function () { console.log(i); } } arr[0](); //0 arr[1](); //1 // 由于let声明的i存在自己的块级作用域,所以本质想循环中的i不会公用,会分别在自己的块级空间中存在一个i,当调用了函数时,函数会首先从自己的块级作用域中向上进行查找i,所以输出的时自己所在的块级作用域中的i的值
const SUM = 30; const TEST; // 没有进行初始化将会报错 const SUM = 40; // 可以重新声明来改变值
const arr = [100, 200]; arr[0] = 'a'; //此操作不会改变arr的内存地址 arr[1] = 'b'; //此操作不会改变arr的内存地址 arr = ['a', 'b']; //此操作会改变arr的内存地址,因此不被允许
ES6中允许从数组中提取值,按照对应位置,对变量赋值。对象也可以实现解构。
let [a, b, c] = [1, 2, 3]; console.log(a); console.log(b); console.log(c);
let person = {name: 'borkyz', age: 20, sex: '男'}; let {name, sex} = person; let {age} = person; console.log(name); console.log(sex); console.log(age);
let person = {name: 'borkyz', age: 20, sex: '男'}; let {name: myName, sex: mySex} = person; console.log(myName); console.log(mySex);
(arg1, arg2, ...) => {code}
let sum = (a, b) => { console.log(a); console.log(b); console.log(a+b); return a+b; } // 传入参数是一个那么可以省略小括号 let put = a => { console.log(a); } // 一行代码并且返回执行结果就可以省略大括号 let plus = (a, b) => a + b;
箭头函数不绑定this,因此箭头函数没有自己的this关键字,如果在箭头函数中调用this,那么this应该指向箭头函数定义位置中的this。
function fn () { console.log(this); return () => { console.log(this); } } const obj = {name: 'brokyz'}; const resFn = fn.call(obj); // 输出结果
var obj = { age: 20, say: () => { alert(this.age); } } obj.say(); // undefined // 箭头函数定义在了obj对象中,由于obj是对象,所以obj没有产生作用域,所以这个箭头函数实际上是被定义在了全局作用域下。因此this指向window,window中没有age所以弹出了undefined
arguments
将会以伪数组的形式存储我们传入函数的所有参数。arguments
。function fn(first, ...args) { console.log(first); // 10 console.log(args); // [20, 3] console.log(arguments); // [10, 20, 30]伪数组 } fn(10, 20, 30); // 箭头函数中无法使用 arguments const sum = (...args) => { let total = 0; args.forEach(item => total += item); return total; }; console.log(sum(10, 20, 30)); // 60
let student = ['wangwu', 'zhangsan', 'lisi']; let [st1, ...st2] = student; console.log(st1); // wangwu console.log(st2); // ['zhangsan', 'lisi']
扩展运算符可以将数组或者对象转为用逗号分隔的参数序列。
let arr = [1, 2, 3]; console.log(...arr); // 1 2 3 // 注意:...arr实际上值是1, 2, 3带有逗号,但是输出时,被当作参数了 console.log(1, 2, 3); // 1 2 3
应用:合并数组
// 方法1 let arr1 = [1, 2, 3]; let arr2 = [4, 5, 6]; let arr3 = [...arr1, ...arr2]; console.log(arr3); // [1, 2, 3, 4, 5, 6] // 方法2 let arr4 = arr1.push(...arr2);
应用:将伪数组转换为真正的数组
let lis = document.querySelectorAll('li'); arrLis = [...lis]; console.log(lis); // NodeList(4) [li, li, li, li] console.log(arrLis); // (4) [li, li, li, li] // 测试数组的方法 arrLis.push('a'); // 未报错 console.log(arrLis); // (5) [li, li, li, li, 'a']
Array.from()
方法可以将类数组或者可遍历的对象转换为真正的数组。Array.from()
可以接受第二个函数参数来对数组进行加工。// 模仿伪数组 let arrayLike = { '0': 'a', '1': 'b', '2': 'c', 'length': 3 } let arr = Array.from(arrayLike); // ['a', 'b', 'c'] console.log(arr); // 可以传入函数对数组进行加工 let arrayLike2 = { '0': 1, '1': 2, '2': 3, 'length': 3 } let arr2 = Array.from(arrayLike2, item => item * 2); // [1, 4, 6] console.log(arr2);
let arr = [{ id: 1, name: '张三' }, { id: 2, name: '李四' }]; let target = arr.find((item, index) => item.id == 2); console.log(target); // {id: 2, name: '李四'}
let arr = [1, 5, 10, 15]; let index = arr.findIndex((vlaue, index) => vlaue > 9); console.log(index); // 2
[1, 2, 3].includes(2); // true [1, 2, 3].includes(4); // false
let name = 'brokyz'; let sayHello = `hello, my name is ${name}`; // hello, my name is brokyz // 模板字符串可以换行 let sayHi = () => 'Hi!'; let html = ` <div> <span>${sayHi()}</span> </div> `; console.log(html);
startsWith()
表示参数字符串是否在原字符产的头部,返回布尔值。endsWith()
表示参数字符串是否在原字符串的尾部,返回布尔值。let str = 'Hello world!'; str.startsWith('Hello') // true str.startsWith('!') // true
repeat()
方法表示将原字符串重复n次,返回一个新字符串。'x'.repeat(3) // 'xxx' 'hello'.repeat(2) // 'hellohello'
const s = new Set();
const set = new Set([1, 2, 3, 4, 4]); console.log(set); // Set(4) {1, 2, 3, 4} console.log(set.size)
const set = new Set([1, 2, 3, 4, 4]); const arr = [...set]; console.log(arr); // [1, 2, 3, 4]
add(value)
:添加某个值,返回 Set 解构本身。delete(value)
:删除某个值,返回一个布尔值,表示删除成功。clear()
:清除所有成员,没有返回值。const set = new Set(); s.add(1).add(2).add(3); // 向set结构中添加值 s.delete(2); // 删除set结构中的2值 s.has(1); // 表示set结构中是否有1这个值,返回布尔值 s.clear() // 清除所有成员,没有返回值
s.forEach(value => console.log(value));
const set = new Set(['a', 'b', 'c']); set.forEach(value => { console.log(value); })