此为ES5新增语法
参考:https://www.runoob.com/js/js-strict.html
参考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode
ES6建议不再使用var
定义变量,而使用let
定义变量,const
定义常量
let a = 1; // 使用 let 定义变量
a = 2; // 变量的值是可修改的
const b = 1; // 使用 const 定义常量
b = 2; // ❌ 报错,常量的值不可修改
对于开发的影响:均使用const,实在需要修改变量,再改为let
无论是let还是const,它们均解决了长久以来变量定义的问题,使用它们定义变量,具有以下特点:
-
全局定义的变量不再作为属性添加到全局对象中
-
在变量定义之前使用它会报错
-
不可重复定义同名变量
-
使用
const
定义变量时,必须初始化 -
变量具有块级作用域,在代码块之外不可使用
注意,在for循环中使用let定义变量,变量所在的作用域是循环体,因此在循环外不能使用。另外,for循环会对该变量做特殊处理,让每次循环使用的都是一个独立的循环变量,这可以解决JS由来已久的问题。
// 过去的问题 for(var i = 0; i < 3; i++){ setTimeout(function(){ console.log(i) }, 1000) } // 输出:3 3 3 // 过去的解决办法:IIFE for(var i = 0; i < 3; i++){ (function(i){ setTimeout(function(){ console.log(i) }, 1000) })(i) } // 输出:0 1 2 // 现在的做法 for(let i = 0; i < 3; i++){ setTimeout(function(){ console.log(i) }, 1000) } // 输出:0 1 2
2 ** 3 // 8
2 ** 4 // 16
API | 含义 |
---|---|
String.prototype.includes(s) | 字符串中是否包含某个子串 |
String.prototype.trim() | 去掉字符串首尾空白字符 |
String.prototype.trimStart() | 去掉字符串开始的空白字符 |
String.prototype.trimEnd() | 去掉字符串末尾的空白字符 |
String.prototype.replaceAll(s, t) | 将字符串中所有的s替换为t |
String.prototype.startsWith(s) | 判断字符串是否以s开头 |
String.prototype.endsWith(s) | 判断字符串是否以s结尾 |
ES6提供了一种新的字符串字面量的书写方式,即模板字符串,写法为
`字符串内容`
模板字符串可以轻松的实现换行和拼接
const user = { name: 'monica', age: 17 }
const s1 = `姓名:${user.name},年龄:${user.age}
my name is ${user.name}`;
// 等同于
const s2 = '姓名:' + user.name + ',年龄:' + user.age + '\nmy name is ' + user.name
/*
* s1和s2均为:
* 姓名:monica,年龄:17
* my name is monica
*/
在需要换行或拼接时,模板字符串远胜于普通字符串
通常,我们可以使用模板字符串拼接html
const user = { name: 'monica', age: 17 }
const html = `
<div>
<p><span class="k">姓名</span><span class="v">${user.name}</span></p>
<p><span class="k">年龄</span><span class="v">${user.age}</span></p>
</div>
`;
/*
* <div>
* <p><span class="k">姓名</span><span class="v">monica</span></p>
* <p><span class="k">年龄</span><span class="v">17</span></p>
* </div>
*/
ES6提供了一种爽翻天的方式遍历各种数组和伪数组
示例1:
const arr = ['a', 'b', 'c']
// 过去的方式——垃圾
for(let i = 0; i < arr.length; i++){
const item = arr[i]
console.log(item)
}
// for of 的方式,结果一样
for(const item of arr){
console.log(item)
}
示例2:
const elements = document.querySelectorAll('.item');
// for of 的方式
for(const elem of elements){
// elem 为获取到的每一个元素
}
API | 作用 | 图示 |
---|---|---|
Array.isArray(target) | 判断target是否为一个数组 | |
Array.from(source) | 将某个伪数组source转换为一个真数组返回 | |
Array.prototype.fill(n) | 将数组的某些项设置为n | |
Array.prototype.forEach(fn) | 遍历数组,传入一个函数,每次遍历会运行该函数 | |
Array.prototype.map(fn) | 数组映射,传入一个函数,映射数组中的每一项 | |
Array.prototype.filter(fn) | 数组筛选,传入一个函数,仅保留满足条件的项 | |
Array.prototype.reduce(fn) | 数组聚合,传入一个函数,对数组每一项按照该函数的返回聚合 | |
Array.prototype.some(fn) | 传入一个函数,判断数组中是否有至少一个通过该函数测试的项 | |
Array.prototype.every(fn) | 传入一个函数,判断数组中是否所有项都能通过该函数的测试 | |
Array.prototype.find(fn) | 传入一个函数,找到数组中第一个能通过该函数测试的项 | |
Array.prototype.includes(item) | 判断数组中是否存在item,判定规则使用的是Object.is |
|
在某些场景下,ES6提供了一种更加简洁的方式书写对象成员
示例1:
const name = 'monica', age = 17;
const sayHello = function(){
console.log(`my name is ${this.name}`);
}
// 过去的方式
const user = {
name: name,
age: age,
sayHello: sayHello
}
// 速写
const user = {
name,
age,
sayHello
}
示例2:
// 过去的方式
const MyMath = {
sum: function(a, b){
//...
},
random: function(min, max){
//...
}
}
// 速写
const MyMath = {
sum(a, b){
// ...
},
random(min, max){
// ...
}
}
ES6提供了一种特殊的语法,通过该语法,可以轻松的从数组或对象中取出想要的部分
示例1:
const user = {
name: 'monica',
age: 17,
addr: {
province: '黑龙江',
city: '哈尔滨'
}
}
// 取出 user 中的 name 和 age
const { name, age } = user;
console.log(name, age); // monica 17
// 取出 user 中的 city
const { addr: { city } } = user
console.log(city); // 哈尔滨
示例2:
const arr = [1, 2, 3, 4]
// 取出数组每一项的值,分别放到变量a、b、c、d中
const [a, b, c, d] = arr;
// 仅取出数组下标1、2的值
const [, a, b] = arr;
// 仅取出数组下标1、3的值
const [, a, , b] = arr;
// 取出数组前两位的值,放到变量a和b中,剩下的值放到一个新数组arr2中
const [a, b, ...arr2] = arr;
示例3:
let a = 1, b = 2;
// 交换两个变量
[b, a] = [a, b]
示例4:
// 在参数位置对传入的对象进行解构
function method({a, b}){
console.log(a, b)
}
const obj = {
a:1,
b:2,
c:3
}
method(obj); // 1 2
示例5:
// 箭头函数也可以在参数位置进行解构
const method = ({a, b}) => {
console.log(a, b)
}
const obj = {
a:1,
b:2,
c:3
}
method(obj); // 1 2
示例6:
const users = [
{name:'monica', age:17},
{name:'邓哥', age:70}
]
// 在遍历时进行解构
for(const {name, age} of users){
console.log(name, age)
}
示例1:
const arr = [3, 6, 1, 7, 2];
// 对数组的展开
Math.max(...arr); // 相当于:Math.max(3, 6, 1, 7, 2)
示例2:
const o1 = {
a: 1,
b: 2,
}
const o2 = {
a: 3,
c: 4,
}
// 对对象的展开
const o3 = {
...o1,
...o2
}
/*
o3:{
a: 3,
b: 2,
c: 4
}
*/
示例3:
const arr = [2,3,4];
const arr2 = [1, ...arr, 5]; // [1,2,3,4,5]
示例4:
const user = {
name: 'monica',
age: 17
}
const user2 = {
...user,
name: '邓哥'
}
// user2: { name:'邓哥', age: 17 }
对于对象中的每个成员,JS使用属性描述符来描述它们
const user = {
name: 'monica',
age: 17
}
上面的对象,在JS内部被描述为
{
// 属性 name 的描述符
name: {
value: 'monica',
configurable: true, // 该属性的描述符是否可以被重新定义
enumerable: true, // 该属性是否允许被遍历,会影响for-in循环
writable: true // 该属性是否允许被修改
},
// 属性 age 的描述符
age: {
value: 'monica',
configurable: true, // 该属性的描述符是否可以被重新定义
enumerable: true, // 该属性是否允许被遍历,会影响for-in循环
writable: true // 该属性是否允许被修改
},
}
ES5提供了一系列的API,针对属性描述符进行操作
-
Object.getOwnPropertyDescriptor(obj, propertyName)
该方法用于获取一个属性的描述符
const user = { name: 'monica', age: 17 } Object.getOwnPropertyDescriptor(user, 'name'); /* { value: 'monica', configurable: true, // 该属性的描述符是否可以被重新定义 enumerable: true, // 该属性是否允许被遍历,会影响for-in循环 writable: true // 该属性是否允许被修改 } */
-
Object.defineProperty(obj, propertyName, descriptor)
该方法用于定义某个属性的描述符
const user = { name: 'monica', age: 17 }; Object.defineProperty(obj, 'name', { value: '邓哥', // 将其值进行修改 enumerable: false, // 让该属性不能被遍历 writable: false // 让该属性无法被重新赋值 })
属性描述符中有两个特殊的配置,分别为get
和set
,通过它们,可以把属性的取值和赋值变为方法调用
const obj = {};
Object.defineProperty(obj, 'a', {
get(){ // 读取属性a时,得到的是该方法的返回值
return 1;
},
set(val){ // 设置属性a时,会把值传入val,调用该方法
console.log(val)
}
})
console.log(obj.a); // 输出:1
obj.a = 3; // 输出:3
console.log(obj.a); // 输出:1
Object.keys(obj)
:获取对象的属性名组成的数组
Object.values(obj)
:获取对象的值组成的数组
Object.entries(obj)
:获取对象属性名和属性值组成的数组
Object.fromEntries(entries)
:将属性名和属性值的数组转换为对象
示例:
const user = {
name: 'monica',
age: 17
}
Object.keys(user); // ["name", "age"]
Object.values(user); // ["monica", 17]
Object.entries(user); // [ ["name", "monica"], ["age", 17] ]
Object.fromEntries([ ["name", "monica"], ["age", 17] ]); // {name:'monica', age:17}
使用Object.freeze(obj)
可以冻结一个对象,该对象的所有属性均不可更改
const obj = {
a: 1,
b: {
c: 3,
},
};
Object.freeze(obj); // 冻结对象obj
obj.a = 2; // 不报错,代码无效
obj.k = 4; // 不报错,代码无效
delete obj.a; // 不报错,代码无效
obj.b = 5; // 不报错,代码无效
obj.b.c = 5; // b对象没有被冻结,有效
console.log(obj); // {a:1, b:{ c: 5 } }
可以使用Object.isFrozen
来判断一个对象是否被冻结
Object.is
方法,可以判断两个值是否相同,它和===
的功能基本一致,区别在于:
- NaN和NaN相等
- +0和-0不相等
Object.is(1, 2); // false
Object.is("1", 1); // false
Object.is(NaN, NaN); // true
Object.is(+0, -0); // false
ES6新增了Set结构,用于保存唯一值的序列
ES6新增了Map结构,用于保存键值对的映射,它和对象的最大区别在于:对象的键只能是字符串,而Map的键可以是任何类型
所有使用函数表达式的位置,均可以替换为箭头函数
箭头函数语法:
// 完整语法
(参数列表) => { 函数体 }
// 若有且仅有一个参数
参数 => { 函数体 }
// 若函数体有且仅有一条返回语句
(参数列表) => 返回语句
示例1:
const sum = function(a, b) {
return a + b;
}
// 箭头函数写法
const sum = (a, b) => a + b
示例2:
dom.onclick = function(e){
// ....
}
// 箭头函数写法
dom.onclick = e => {
// ...
}
示例3:
setTimeout(function(){
// ...
}, 1000)
// 箭头函数写法
setTimeout(() => {
// ...
}, 1000)
箭头函数有以下特点:
-
不能使用
new
调用 -
没有原型,即没有
prototype
属性 -
没有
arugments
-
没有
this
有些教程中会说:箭头函数的
this
永远指向箭头函数定义位置的this
,因为箭头函数会绑定this
。这个说法没错,根本原因是它没有
this
,它里面的this
使用的是外层的this
const counter = { count: 0, start: function(){ // 这里的 this 指向 counter setInterval(() => { // 这里的 this 实际上是 start 函数的 this this.count++; }, 1000) } }
箭头函数的这些特点,都足以说明:箭头函数特别适用于那些临时需要函数的位置
我们将来会在面试指导阶段对this指向进行总结
ES6不建议再使用arguments
来获取参数列表,它推荐使用剩余参数来收集未知数量的参数
// ...args为剩余参数
function method(a, b, ...args){
console.log(a, b, args)
}
method(1, 2, 3, 4, 5, 6, 7); // 1 2 [3, 4, 5, 6, 7]
method(1, 2); // 1 2 []
注意,剩余参数只能声明为最后一个参数
ES6提供了参数默认值,当参数没有传递或传递为undefined
时,会使用默认值
示例1:
// 对参数 b 使用了默认值1
function method(a, b = 1){
console.log(a, b)
}
method(1, 2); // 1 2
method(1); // 1 1
method(1, undefined); // 1 1
示例2:
// 对参数 b 使用了默认值1, 对参数 c 使用默认值2
const method = (a, b = 1, c = 2, d) => {
console.log(a, b, c, d)
}
method(1, 2); // 1 2 2 undefined
method(1); // 1 1 2 undefined
method(1, undefined, undefined, 4); // 1 1 2 4
过去,函数有着两种调用方式:
function A(){}
A(); // 直接调用
new A(); // 作为构造函数调用
这种做法无法从定义上明确函数的用途,因此,ES6推出了一种全新的语法来书写构造函数
示例1:
// 旧的写法
function User(firstName, lastName){
this.firstName = firstName;
this.lastName = lastName;
this.fullName = `${firstName} ${lastName}`;
}
User.isUser = function(u){
return !!u && !!u.fullName
}
User.prototype.sayHello = function(){
console.log(`Hello, my name is ${this.fullName}`);
}
// 新的等效写法
class User{
constructor(firstName, lastName){
this.firstName = firstName;
this.lastName = lastName;
this.fullName = `${firstName} ${lastName}`;
}
static isUser(u){
return !!u && !!u.fullName
}
sayHello(){
console.log(`Hello, my name is ${this.fullName}`);
}
}
示例2:
function Animal(type, name){
this.type = type;
this.name = name;
}
Animal.prototype.intro = function(){
console.log(`I am ${this.type}, my name is ${this.name}`)
}
function Dog(name){
Animal.call(this, '狗', name);
}
Dog.prototype = Object.create(Animal.prototype); // 设置继承关系
// 新的方式
class Animal{
constructor(type, name){
this.type = type;
this.name = name;
}
intro(){
console.log(`I am ${this.type}, my name is ${this.name}`)
}
}
class Dog extends Animal{
constructor(name){
super('狗', name);
}
}
API | 含义 |
---|---|
Function.prototype.call(obj, ...args) | 调用函数,绑定this为obj 后续以列表的形式提供参数 |
Function.prototype.apply(obj, args) | 调用函数,绑定this为obj args以数组的形式提供参数 |
Function.prototype.bind(obj, ...args) | 返回一个函数的拷贝 新函数的this被绑定为obj 起始参数被绑定为args |