let与var
let 关键字声明变量时,其作用域仅限于该代码块、语句或表达式。
var 关键字声明变量时,它是全局声明的,如果在函数内部声明则是局部声明的。
let 暂时性死区
利用let声明的变量会绑定在这个块级作用域,不会受外界的影响
var tmp = 123;
if (true) {
tmp = 'abc';
let tmp;
}
var与let的经典面试题
var arr = [];
for (var i = 0; i < 2; i++) {
arr[i] = function () {
console.log(i);
}
}
arr[0](); //2
arr[1](); //2
解: 此题的关键点在于变量i是全局的,函数执行时输出的都是全局作用域下的i值。
let arr = [];
for (let i = 0; i < 2; i++) {
arr[i] = function () {
console.log(i);
}
}
arr[0](); //0
arr[1](); //1
解: 此题的关键点在于每次循环都会产生一个块级作用域,每个块级作用域中的变量都是不同的,函数执行时输出的是自己上一级(循环产生的块级作用域)作用域下的i值.
const
- 具有块级作用域
- 声明常量,常量名全部大写
const s = [5, 6, 7];
s = [1, 2, 3]; //出错,不能使用赋值运算符
s[2] = 45; //可以
-Object.freeze(变量)
const声明的常量依旧可以被改变
为了确保数据不被改变,JavaScript 提供了一个函数 Object.freeze()
let obj = {
name:"FreeCodeCamp",
review:"Awesome"
};
Object.freeze(obj);
箭头函数
- 没有参数的
const myFunc = () => {
const myVar = "value";
return myVar;
}
当不需要函数体,只返回一个值的时候,箭头函数允许你省略 return 关键字和外面的大括号。
const myFunc = () => "value";
- 有参数
const doubler = (item) => item * 2;
doubler(4);
- 有默认参数
const greeting = (name = "Anonymous") => "Hello " + name;
this
- 箭头函数中不绑定this,箭头函数中的this指向是它所定义的位置,可以简单理解成,定义箭头函数中的作用域的this指向谁,它就指向谁
- 箭头函数的优点在于解决了this执行环境所造成的一些问题。比如:解决了匿名函数this指向的问题(匿名函数的执行环境具有全局性),包括setTimeout和setInterval中使用this所造成的问题
const obj = { name: '张三'}
function fn () {
console.log(this);//this 指向 是obj对象
return () => {
console.log(this);//obj
//this 指向的是箭头函数定义的位置,那么这个箭头函数定义在fn里面
//而这个fn指向是的obj对象,所以这个this也指向是obj对象
}
}
const resFn = fn.call(obj);
resFn()
关于箭头函数中的this的经典面试题
var age = 100;
var obj = {
age: 20,
say: () => {
alert(this.age)
}
}
obj.say();//100
//箭头函数this指向的是被声明的作用域里面,而对象没有作用域的,所以箭头函数虽然在对象中被定义,但是this指向的是全局作用域
rest操作符(…)接受若干个参数
剩余参数语法允许我们将一个不定数量的参数表示为一个数组,不定参数定义方式,这种方式很方便的去声明不知道参数情况下的一个函数
const sum = (...args) => {
return args.reduce((a, b) => a + b, 0);
}
//sum() 0
//sum(0,1,2) 3
//sum(4,5) 9
function sum (first, ...args) {
console.log(first); // 10
console.log(args); // [20, 30]
}
sum(10, 20, 30)
解构赋值
中允许从数组中提取值,按照对应位置,对变量赋值,对象也可以实现解构
获取对象的值
const user = { name: 'John Doe', age: 34 };
//ES5
const name = user.name;
const age = user.age;
//ES6 解构赋值
const { name, age } = user;
从对象中分配变量
const user = { name: 'John Doe', age: 34 };
//userName是user中的name的新赋值的变量名
const { name: userName, age: userAge } = user;
从嵌套对象中分配变量
const user = {
johnDoe: {
age: 34,
email: 'johnDoe@freeCodeCamp.com'
}
};
//解构对象的属性值赋值给具有相同名字的变量
const { johnDoe: { age, email }} = user;
//将对象的属性值赋值给具有不同名字的变量
const { johnDoe: { age: userAge, email: userEmail }} = user;
从数组中分配变量
const [a, b] = [1, 2, 3, 4, 5, 6];
console.log(a, b); //1 2
const [a, b,,, c] = [1, 2, 3, 4, 5, 6];
console.log(a, b, c);//1 2 5
配合 rest 操作符来重新分配数组元素
const [a, b, ...arr] = [1, 2, 3, 4, 5, 7];
console.log(a, b); //1,2
console.log(arr); //[3, 4, 5, 7]
将对象作为函数的参数传递
//ES5
const profileUpdate = (profileData) => {
const { name, age, nationality, location } = profileData;
}
//ES6
const profileUpdate = ({ name, age, nationality, location }) => {
}
//当 profileData 被传递到上面的函数时,从函数参数中解构出值以在函数内使用。
Array 的扩展方法(★★)
展开操作符
…arr 返回一个解压的数组。 但展开操作符只能够在函数的参数中或者数组中使用。
//直接复制数组
const arr1 = ['JAN', 'FEB', 'MAR', 'APR', 'MAY'];
let arr2 = [...arr1];
//找出一个数组中最大的数
const arr = [6, 89, 3, 45];
const maximus = Math.max(...arr);
//或
var arr = [6, 89, 3, 45];
var maximus = Math.max.apply(null, arr);
//合并数组
let ary1 = [1, 2, 3];
let ary2 = [3, 4, 5];
let ary3 = [...ary1, ...ary2];
ary1.push(...ary2);
//将类数组或可遍历对象转换为真正的数组
let oDivs = document.getElementsByTagName('div');
oDivs = [...oDivs];
Math.max(arr); //报错
const spreaded = ...arr; //报错
构造函数方法: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
String 的扩展方法
模板字面量创建字符串
const person = {
name: "Zodiac Hasbro",
age: 56
};
//注意:使用的是``反引号,而不是单引号
//其次,多行也无需换行符
//再者,使用${variable}占位符,而不是+连接符号了
const greeting = `Hello, my name is ${person.name}!
I am ${person.age} years old.`;
console.log(greeting);
//Hello, my name is Zodiac Hasbro!
//I am 56 years old.
//在模板字符串中可以调用函数
const sayHello = function () {
return '哈哈哈哈 追不到我吧 我就是这么强大';
};
let greet = `${sayHello()} 哈哈哈哈`;
console.log(greet); // 哈哈哈哈 追不到我吧 我就是这么强大 哈哈哈哈
例子: 使用模板字符串和表达式内插的方法令返回的数组为
[
'<li class="text-warning">no-var</li>',
'<li class="text-warning">var-on-top</li>',
'<li class="text-warning">linebreak</li>'
]
const result = {
success: ["max-length", "no-amd", "prefer-arrow-functions"],
failure: ["no-var", "var-on-top", "linebreak"],
skipped: ["no-extra-semi", "no-dup-keys"]
};
function makeList(arr) {
// 只修改这一行下面的代码
const failureItems = [];
for(let item of arr){
failureItems.push(`<li class="text-warning">${item}</li>`);
}
// 只修改这一行上面的代码
return failureItems;
}
const failuresList = makeList(result.failure);
实例方法:startsWith() 和 endsWith()
- startsWith():表示参数字符串是否在原字符串的头部,返回布尔值
- endsWith():表示参数字符串是否在原字符串的尾部,返回布尔值
let str = 'Hello world!';
str.startsWith('Hello') // true
str.endsWith('!') // true
实例方法: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、delete、has、clear)
- 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
//原:
const getMousePosition = (x, y) => ({
x: x,
y: y
});
//ES6:消除了类似 x: x 这种冗余的写法
const getMousePosition = (x, y) => ({ x, y });
编写简洁的函数声明
//原:
const person = {
name: "Taylor",
sayHello: function() {
return `Hello! My name is ${this.name}.`;
}
};
//ES6:删除 function 关键词和冒号
const person = {
name: "Taylor",
sayHello() {
return `Hello! My name is ${this.name}.`;
}
};
class 语法定义构造函数
在 ES5 里面,我们通常会定义一个构造函数 constructor,然后使用 new 关键字来实例化一个对象:
var SpaceShuttle = function(targetPlanet){
this.targetPlanet = targetPlanet;
}
var zeus = new SpaceShuttle('Jupiter');
class 语法只是简单地替换了构造函数 constructor 的写法:
class SpaceShuttle {
constructor(targetPlanet) {
this.targetPlanet = targetPlanet;
}
}
const zeus = new SpaceShuttle('Jupiter');
getter 和 setter 来控制对象的访问
Getter 函数的作用是可以让对象返回一个私有变量,而不需要直接去访问私有变量。
Setter 函数的作用是可以基于传进的参数来修改对象中私有变量。 这些修改可以是计算,或者是直接替换之前的值。
class Book {
constructor(author) {
//通常会在私有变量前添加下划线(_)。 然而,这种做法本身并不是将变量变成私有的。
this._author = author;
}
// getter
get writer() {
return this._author;
}
// setter
set writer(updatedAuthor) {
this._author = updatedAuthor;
}
}
const novel = new Book('anonymous');
console.log(novel.writer);
novel.writer = 'newAuthor';
console.log(novel.writer);
创建一个模块脚本
<html>
<body>
<!-- 只修改这一行下面的代码 -->
<script type="module" src="index.js"></script>
<!-- 只修改这一行上面的代码 -->
</body>
</html>
export 来重用代码块
假设有一个文件 math_functions.js,该文件包含了数学运算相关的一些函数。 其中一个存储在变量 add 里,该函数接受两个数字作为参数返回它们的和。 你想在几个不同的 JavaScript 文件中使用这个函数。 要实现这个目的,就需要 export 它。
export const add = (x, y) => {
return x + y;
}
//或
const add = (x, y) => {
return x + y;
}
export { add };
export { add, subtract };
import 复用 JavaScript 代码
import 可以导入文件或模块的一部分。
import { add , subtract } from './math_functions.js';
在这里,import
会在 math_functions.js
里找到 add,只导入这个函数,忽略剩余的部分。 ./ 告诉程序在当前文件的相同目录寻找 math_functions.js
文件。 用这种方式导入时,相对路径(./)和文件扩展名(.js)都是必需的。
用 * 从文件中导入所有内容
import * as myMathModule from "./math_functions.js";
上面的 import 语句会创建一个叫作 myMathModule 的对象。 这只是一个变量名,可以随便命名。 对象包含 math_functions.js 文件里的所有导出,可以像访问对象的属性那样访问里面的函数。 下面是使用导入的 add 和 subtract 函数的例子:
myMathModule.add(2,3);
myMathModule.subtract(5,3);
export default 默认导出
默认导出的 export 的语法。 在文件中只有一个值需要导出的时候,通常会使用这种语法。 它也常常用于给文件或者模块创建返回值。
//命名函数
export default function add(x, y) {
return x + y;
}
//匿名函数
export default function(x, y) {
return x + y;
}
export default 用于为模块或文件声明一个返回值,在每个文件或者模块中应当只默认导出一个值。 此外,你不能将 export default 与 var、let 或 const 同时使用。
导入默认的导出
import add from "./math_functions.js";
这个语法有一处特别的地方, 被导入的 add 值没有被花括号({})所包围。 add 只是一个变量的名字,对应 math_functions.js 文件的任何默认导出值。 在导入默认导出时,可以使用任何名字。
Promise
Promise 是异步编程的一种解决方案 - 它在未来的某时会生成一个值。当程序需要花费未知的时间才能完成时(比如一些异步操作),一般是服务器请求,promise就很有用。 任务完成,分执行成功和执行失败两种情况。 Promise 是构造器函数,需要通过 new 关键字来创建。 构造器参数是一个函数,该函数有两个参数 - resolve 和 reject。 通过它们来判断 promise 的执行结果。
Promise 有三个状态:pending、fulfilled 和 rejected。Promise 提供的 resolve 和 reject 参数就是用来结束 promise 的。 Promise 成功时调用 resolve,promise 执行失败时调用 reject。
当promise发起请求后,服务器请求会花费一些时间,当结束时,需要根据服务器的响应执行一些操作。 这可以用 then 方法来实现。当 promise 完成 resolve 时会触发 then 方法。
当 promise 失败时会调用 catch 方法。 当 promise 的 reject 方法执行时会直接调用。
const myPromise = new Promise((resolve, reject) => {
if(condition here) {
resolve("Promise was fulfilled");
} else {
reject("Promise was rejected");
}
});
myPromise.then(result => {
});
myPromise.catch(error => {
});