ES5
严格模式
介绍
ES5除了正常的运行模式(又称为混杂模式),还添加了第二种运行模式:“严格模式”(strict mode)。严格模式顾名思义,就是使JavaScript在更严格语法条件下运行。
作用
- 消除JavaScript语法的一些不合理,不严谨之处,减少一些怪异行为
- 消除代码运行的一些不安全之处,保证代码运行的安全
- 为未来新版本的JavaScript做好铺垫
使用
- 在全局或函数的第一条语句定义为:‘use strict’
- 如果浏览器不支持,只解析为一条简单的语句,没有任何副作用
// 全局使用严格模式
'use strict';
girl = 'Saber';
// 函数中使用严格模式
function main(){
'use strict';
boy = 'Archer';
}
main()
语法和行为改变
- 必须用
var
声明变量,不允许使用未声明的变量 - 禁止自定义函数中的
this
指向window
eval
作用域eval
是一个函数,接收字符串参数,会对字符串进行js语法解析并运行- 对象不能有重复的属性
- 严格模式下 函数不允许有同名的参数
- 新增一些保留字
private
protected
implements
interface
public
Object 扩展方法
Object.create(prototype,[descriptors])
Object.create 方法可以指定对象为原型创建新的对象,同时可以为新的对象设置属性,并对属性进行描述
value
:指定值writable
:标识当前属性是否可修改,默认false
configurable
:标识当前属性是否可以被删除,默认false
enumerable
:标识当前属性是否能用for in
枚举,默认false
get
:当获取当前属性是的回调函数set
:当设置当前属性时的回调函数
// 创建一个汽车对象
var car = {
name: '汽车',
run: function(){
console.log('正在行驶!');
}
};
// 以 car 为原型对象创建新对象 继承
var aodi = Object.create(car, {
brand: {
// 设置属性值
value: '奥迪',
// 属性值是否可修改
writable: false,
// 属性是否可删除
configurabel: false,
// 属性是否可以枚举(被遍历出来)
enumrable: false
},
price: {
// get 是一个对象的方法,无需手动调用
get: function(){
console.log('Price被获取了')
// 函数的返回结果作为该属性的值
return 299999;
},
// set 设置 可对设置的值进行校验
set: function(value){
// this指的是新创建的对象
this.jiage = value
console.log(`值被修改为${value}`)
}
}
})
以上两种方式只能择其一
Object.defineProperties(object,descriptors)
直接在一个对象上定义新的属性或修改现有属性,并返回该对象。
- object 要操作的对象
- descriptors 属性的描述
- get 作为该属性的getter函数,如果没有戈塔特人则为undefined。函数返回值将被用作属性的值
- set 作为属性的setter函数,如果没有setter则为undefined。函该将仅接受参数赋值给该属性的新值
// 定义对象
var star = {
firstName: '刘',
lastName: '德华'
}
// 添加对象属性
Object.defineProperties(star,{
fullName: {
get: function(){
// this 指向传入的star
return this.firstName + this.lastName
}
}
})
console.log(star.fullName)
call apply 和 bind
- call 方法使用一个指定的this值和单独给出的一个或多个参数来调用一个函数
- apply 方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数
- bind 同call相思,不过该方法会返回一个新的函数,而不会立即执行
function main(){
console.log(this)
}
// 1.直接调用函数
main() // window
// 2.创建一个对象
var company = {
name: 'Saber',
age: 18
}
// 使用这个对象调用main方法
main.call(company) // company
main.apply(company) // company
// bind 修改 this 的值,返回一个新的函数
var fn = main.bind(company)
fn() // company
ES6
新特性
let 关键字
let 关键字用来声明变量,使用let声明的变量有几个特点:
- 不允许重复声明
- 块级作用域
- 不存在变量提升
- 不影响作用域链
声明变量使用let
const 关键字
const 关键字用来声明常量,const声明有以下特点:
- 声明的时候一定要赋初值
- 常量的名称一把为【大写】
- 不能修改常量的值
- 不允许重复声明
- 块级作用域
- 关于数组对象和元素的修改(引用类型值修改不会报错)
声明对象类型使用const,非对象类型声明使用let
变量的解构赋值
ES6允许按照一定模式从数组和对象中提取值,对变量进行赋值,这被称为结构赋值
// 数组的结构赋值
const arr = ['Saber','Archer','Rider','Lancer']
let [S,A,R,L] = arr
// 对象的结构赋值
const star = {
name: '于谦',
tags: ['抽烟','喝酒','烫头'],
say: function(){
console.log('说相声')
}
}
let {name,tags,say} = star
模板字符串
模板字符串(template string)是增强版的字符串,用反引号(`)标识,特点:
- 字符串中可以出现换行符
- 可以使用${variable}形式输出变量
let str = `<ul>
<li>Saber</li>
<li>Archer</li>
<li>Rider</li>
</ul>`
let master = "卫宫士郎"
let say = `你就是我的master吗,${master}`
简化对象写法
let name = 'Saber'
let age = 18
let skill = function(){
console.log('ex咖喱棒')
}
const servant = {
name,
age,
skill,
tag(){
console.log('我是吃货')
}
}
console.log(servant)
箭头函数
- 声明格式
- 函数调用
let sum = (a,b,c) => {
let result = a + b + c
return result
}
sum()
// sum.call({},1,2,3)
// sum.apply({},[1,2,3])
特点
- this 的值是静态的(指向声明时作用域的this)
- 不能作为构造函数使用
- 不能使用 arguments
- 箭头函数简写
- 不写小括号,当形参有且只有一个
- 不写大括号,当代吗体只有一条语句且语句的指向结果为函数返回值 return也可以省略
let rand = (m,n) => Math.floor(Math.random() * (n - m + 1)) + m - 1
如果回调与this的值相关的,则不能使用箭头函数
例:事件的回调、对象的方法
参数默认值
ES6 允许给函数参数赋值初始值
- 参数直接设置默认值 具有默认值的参数,位置一般靠后
- 与结构赋值结合使用 结构赋值的先后顺序不影响
function sum(a,b,c=10){
console.log(a + b + c)
}
function connect({host = '127.0.0.1',port,pwd,dbname}){
console.log(host)
console.log(port)
console.log(pwd)
console.log(dbname)
}
connect({
host: 'localhost',
port: 8080,
pwd: 'root',
dbname: 'project'
})
rest 参数
// arguments
function main(...args){
//1. 使用arguments获取实参
console.log(arguments)
//2. rest参数
console.log(args)
}
main(1,2,3,5,7)
// 多个参数
function fun(a,b,...args){
console.log(a)
console.log(b)
console.log(args)
}
fun(1,9,9,8,1,1,1,8)
针对于不定参数的函数
spread扩展运算符
- 数组的展开
const arr = ['Saber','Archer','Lancer']
function fn(){
console.log(arguments)
}
fn(...arr) // ...arr => ...['Saber','Archer','Lancer']
//fn('Saber','Archer','Lancer')
- 对象的展开
const skillOne = {
q: '天音波'
}
const skillTwo = {
w: '金钟罩'
}
const skillThree = {
e: '天雷破'
}
const skillFour = {
r: '猛龙摆尾'
}
const LeeSin = {...skillOne,...skillTwo,...skillThree,...skillFour}
console.log(LeeSin)
- 应用
// 1.数组合并
const kuaizi = ['肖央','王太利']
const chuanqi = ['曾毅','玲花']
const team = [...kuaizi,...chuanqi]
console.log(team)
// 2.新数组克隆
const arr = ['e','g','m']
const arr2 = [...arr,'x','y','z']
console.log(arr2)
// 3.将伪数组转为真正的数组
const divs = document.querySelectorAll('div')
const result = [...divs]
console.log(result)
Symbol
ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。它是JavaScript语言的第七种数据类型,是一种类似于字符串的数据类型
Symbol特点
- Symbol的值是唯一的,用来解决命名冲突的问题
- Symbol值不能与其他数据进行运算
- Symbol定义的UI对象属性不能使用for…in循环遍历,但是可以使用Reflect.ownKeys来获取对象的所有键名
// 创建Symbol
let s1 = Symbol()
console.log(s1,typeof s1)
// 添加标识的Symbol
let s2 = Symbol('Saber')
let s3 = Symbol('Saber')
console.log(s2 === s3)//false
// 使用Symbol for定义
let s4 = Symbol.for('1001')
let s5 = Symbol.for('1001')
console.log(s4 === s5)//true
Symbol类型唯一合理的用法是用变量存储Symbol的值,然后使用存储的值创建对象属性
Symbol 创建对象属性
// 向对象中添加方法 up down
const method = {
up: Symbol(),
down: Symbol()
}
let str = 'run'
const game = {
name: '英雄联盟',
up: function(){
},
// 内部添加方法
[str]: function(){
},
[method.up]: function(){
console.log('up up up')
}
}
// 外部添加方法
game[method.down] = function () {
console.log("down down down")
}
game[method.up]()
game[method.down]()
Symbol 内置值
除了定义自己使用的Symbol值以外,ES6还提供了11个内置的Symbol值,指向语言内部使用的方法。
Symbol.hasInstance | 当其他对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法 |
---|---|
Symbol.isConcatSpreadable | 对象的Symbol.isConcatSpreadable属性等于的是一个布尔值,表示该对象用于Array.prototype.concat()时,是否可以展开。 |
Symbol.unscopables | 该对象指定了使用with关键字时,哪些属性会被with环境排除。 |
Symbol.match | 当执行str.match(myObject)时,如果该属性存在,会调用它,返回该方法的返回值。 |
Symbol.replace | 当该对象被str.replace(myObject)方法调用时,会返回该方法的返回值。 |
Symbol.search | 当该对象被str.search(myObject)方法调用时,会返回该方法的返回值 |
Symbol.split | 当该对象被str.split(myObject)方法调用时,会返回该方法的返回值。 |
Symbol.iterator | 对象进行for…of循环时,会调用Symbol.iterator方法,返回该对象的默认遍历器 |
Symbol.toPrimitive | 该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。 |
Symbol.toStringTag | 在该对象上面调用toString方法时,返回该方法的返回值 |
Symbol.species | 创建衍生对象时,会使用该属性 |
在特定条件下会执行
迭代器
迭代器(iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署iterator接口,就可以完成遍历操作。
- ES6创造了一种新的遍历命令for…of循环,iterator接口主要供for…of消费
- 原生具备iterator接口的数据(可用for of 遍历)
- Array
- Arguments
- Set
- Map
- String
- TypedArray
- NodeList
- 工作原理
- 创建一个指针对象,指向当前数据结构的起始位置
- 第一次调用对象的next方法,指针一直往后移动,直到指向最后一个成员
- 每调用next方法返回一个包含value和done属性的对象
const servant = ["Saber", "Archer", "Rider", "Lancer"];
for (const item of servant) {
console.log(item);
}
const iterator = servant[Symbol.iterator]();
console.log(iterator);
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
const team = {
name: "TES",
members: ["369", "knight", "karsa", "jacklove", "yuyanjia"],
//添加 Symbol.iterator
[Symbol.iterator]: function () {
let index = 0;
return {
next: () => {
//声明对象
const result = {
value: this.members[index],
done: false,
};
//判断下标 修改 『done』属性的值
if (index === this.members.length) {
result.done = true;
}
// 下标自增
index++;
// 返回结果
return result;
},
};
},
};
for (const item of team) {
console.log(item);
}
需要自定义遍历数据的时候,要想到迭代器
Promise
Promise 是 ES6引入的异步编程的新解决方案。语法上Promise是一个构造函数, 用来封装异步操作并可以获取其成功或失败的结果。
Set
ES6提供了新的数据结构Set(集合)。它类似于数组,『但成员的值都是唯一的』,集合实现了iterator接口,所以可以使用『扩展运算符』和『for…of』进行遍历集合的属性和方法:
- size 返回集合的元素个数
- add 增加一个新元素,返回当前集合
- delete删除元素,返回boolean值
- has 检测集合中是否包含某个元素,返回boolean值
- clear 清空集合中,返回undefined
// 创建一个空集合
const s = new Set()
// 创建一个非空集合
const s2 = new Set([1,5,9,13,17])
// 1.元素个数
console.log(s2.size)
// 2.添加
s2.add(21)
// 3.删除
s2.delete(5)
// 4.检测集合中是否包含某个元素
console.log(s2.has(9))
// 5.清空
s2.clear()
for( const item of s2){
console.log(item)
}
// 1.数组去重
const arr = [
"大事儿",
"小事儿",
"心里事儿",
"坏事儿",
"小事儿",
"好事儿",
"坏事儿",
];
const s = new Set(arr);
const result = [...s];
console.log(result);
// 2.交集
const arr1 = [1, 3, 5, 1, 2, 4, 5];
const arr2 = [3, 3, 4, 6, 5, 2, 4, 4, 1];
const result2 = [...new Set(arr1)].filter((item) => {
return (new Set(arr2)).has(item);
});
console.log(result2);
// 并集
const result3 = [...new Set([...new Set(arr1), ...new Set(arr2)])];
console.log(result3);
// 差集
const result4 = [...new Set(arr2)].filter((item) => {
return !(new Set(arr1)).has(item);
});
console.log(result4);
Map
ES6提供了Map数据结构。它类似于对象,也是键值对的集合。但是"键"的范围不限于字符串,各种类型的值(包括对象)都可以当做键。Map也实现了iterator接口,所以可以使用扩展运算符
和for...of
进行遍历Map的属性和方法
// 声明 Map
const m = new Map();
// 添加元素
const team = {
name: "Tes",
members: ["369", "knight", "jacklove"],
};
m.set("servant", "Saber");
m.set(team, "LPL");
// 获取元素
console.log(m.get("servant"));
console.log(m.get(team));
// 删除元素
m.delete('servant')
// 检测
console.log(m.has(team));
// 元素个数
console.log(m.size);
// 清空
m.clear()
...
可以展开实现了iterator接口的数据
class 类
ES6提供了更接近传统语言的写法,引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。基本上,ES6的class可以看作指示一个语法糖,他的绝大部分功能,ES5都可以做到,新的class写法只是让对象的原型的写法更加清晰、更像面向对象编程的语法而已
知识点
对象声明
// ES5
// 创建对象的方式 new Object{} 工厂函数 构造函数 Object.create
function Phone(brand,price){
this.brand = brand
this.price = price
}
// 添加方法
Phone.prototype.call = function(someone){
console.log(`给${someone}打电话`);
}
Phone.prototype.sendMessage = function(someone){
console.log(`给${someone}发短信`);
}
// 实例化对象
const nokia = new Phone('诺基亚',224)
// ES6
// 语法
class Phone{
// 构造方法
constructor(brand,price){
this.brand = brand
this.price =price
}
// 方法
call(someone){
console.log(`给${someone}打电话`);
}
sendMessage(someone){
console.log(`给${someone}发短信`);
}
}
const iphone = new Phone('苹果',10000)
- 构造方法不是必须的
- 构造方法只能有一个
静态成员
// ES5
function Phone(brand){
// 为实例对象添加属性
this.brand = brand
}
// 为函数对象添加属性
Phone.name = '手机' // 又称之为静态成员
Phone.change = function(){
console.log("change the world");
}
const nokia = new Phone('诺基亚')
console.log(nokia);
console.dir(Phone)
// ES6
class Phone {
static name = "手机";
static change() {
console.log("change the world");
}
}
console.log(Phone.name);
Phone.change();
静态成员属于类而不属于实例对象
对象继承
// ES5
// 父类
function Phone(brand,price){
this.brand= brand
this.price = price
}
Phone.prototype.call = function(someone){
console.log(`给${someone}打电话`);
}
Phone.prototype.sendMessage = function(someone){
console.log(`给${someone}发短信`);
}
// 子类
function SmartPhone(brand,price,storage,soc){
// 调用父类函数 属性的初始化
Phone.call(this,brand,price)
// 初始化
this.storage = storage
this.soc = soc
}
SmartPhone.prototype = new Phone
SmartPhone.prototype.constructor = SmartPhone
// 添加
SmartPhone.prototype.playGame = function(){
console.log('我亚索贼6');
}
SmartPhone.prototype.takePhone = function(){
console.log('10亿像素,照亮你的丑');
}
const mate40 = new SmartPhone('华为',6999,'256g','Kirin')
console.log(mate40);
mate40.playGame()
mate40.takePhone()
// ES6
class Phone {
// 构造方法
constructor(brand, price) {
this.brand = brand;
this.price = price;
}
// 方法
call(someone) {
console.log(`给${someone}打电话`);
}
sendMessage(someone) {
console.log(`给${someone}发短信`);
}
}
class SmartPhone extends Phone {
// 构造方法
constructor(brand, price, storage, soc) {
// 调用父类的构造方法 属性初始化
super(brand, price);
this.storage = storage;
this.soc = soc;
}
// 子类对象方法
playGame() {
console.log("我亚索贼6");
}
takePhone() {
console.log("10亿像素,照亮你的丑");
}
}
const onePlus = new SmartPhone('一加','3999','128g','Snapdragon888')
onePlus.playGame()
onePlus.sendMessage('马斯克')
onePlus.takePhone()
get 和 set
class Phone{
get price(){
return 2999
}
set price(value){
this.jiage = value
}
static get size(){
return '6.6inch'
}
static set size(value){
this.chicun = value
}
}
const iphone = new Phone()
// 属性获取
console.log(iphone.price);
// 属性设置
iphone.price = 6999
// 属性获取
const res = Phone.size
console.log(res);
// 静态属性的设置
Phone.size = '7.7inch'
console.dir(Phone)
数值扩展
// Number.isFinite 检测一个数值是否为有限数
console.log(Number.isFinite(10)); // false
console.log(Number.isFinite(Math.PI)); // false
console.log(Number.isFinite(1/0)); // true
// Number.isNaN 检测一个数值是否为 NaN isNaN
console.log(Number.isNaN(NaN));// true
// Number.parseInt 字符串转整数
console.log(Number.parseInt('1024奥利给'));// 1024
console.log(Number('1024奥利给')); // NaN
// Math.trunc 将小数部分抹除
console.log(Math.trunc(Math.PI));// 3
// Number.isInteger 判断一个数是否为整数 is 是否 integer 整型
console.log(Number.isInteger(3.14));// false
console.log(Number.isInteger(3));// true
// 幂运算 (ES7) Math.pow()
console.log(2 ** 10);// 1024
对象扩展
// 判断两个值是否完全相等 === Object.is
let n = 100
let n2 = 200
console.log(Object.is(n,n2)); // false
console.log(Object.is(NaN,NaN)); // true
console.log(NaN === NaN); // false
// Object.assign 对象的合并
// 同名属性会覆盖,会合并到第一个对象并返回
const A = {
name: '简自豪'
}
const B= {
team: ['SHR','OMG','QG','RNG'],
name: 'Uzi'
}
const C = {
name: '小狗'
}
const res = Object.assign(A,B,C)
console.log(res === A);// true
console.log(A);
// 直接修改 __proto__ 设置原型
const D = {
name: '父级'
}
const E = {
test: '测试'
}
E.__proto__ = D
console.log(E);
浅拷贝
console.log('-----------1.直接复制-----------');
const arr = [1,2,3,4]
const newArr = arr
newArr[0] = 5
console.log(arr);
console.log(newArr);
console.log('-----------2.数组-----------');
const arr = [{name:'Saber'},2,3,4]
// 1.concat
const newArr = [].concat(arr)
newArr[0].name = '阿尔托莉雅·潘德拉贡'
console.log(arr);
console.log(newArr);
const arr = [{name:'Saber'},2,3,4]
// 2.slice
const newArr = arr.slice(0)
newArr[0].name = '阿尔托莉雅·潘德拉贡'
console.log(arr);
console.log(newArr);
const arr = [{name:'Saber'},2,3,4]
// 3.扩展运算符
const newArr = [...arr]
newArr[0].name = '阿尔托莉雅·潘德拉贡'
console.log(arr);
console.log(newArr);
// 使用assign方法创建对象(assign对象的合并)
const company = {
name: 'Tencent',
position: ['北京','上海','深圳']
}
const newCompany = Object.assign({},company)
newCompany.position[0] = 'beijing'
console.log(company);
console.log(newCompany);
深拷贝
JSON实现深拷贝
// JSON 实现深拷贝
// stringify 将JS对象转为JSON格式的字符串
// parse 将JSON格式的字符串转化为JS对象
// 缺陷:不能复制方法
const company = {
name: "Tencent",
position: ["北京", "上海", "深圳"],
leader: {
name: "小马哥",
},
copy: function(){
console.log('与复制了上千种忍术的我为敌,你毫无胜算')
}
};
// 将对象转为JSON格式字符串
let str = JSON.stringify(company)
// 将对象转为JS对象
let newCompany = JSON.parse(str)
newCompany.position[0] = 'beijing'
console.log(company);
console.log(newCompany);
递归实现深拷贝
// 封装一个函数
function deepClone(data) {
// 创建一个容器
let container;
// 判断
let type = getDataType(data);
if (type === "Object") {
container = {};
}
if (type === "Array") {
container = [];
}
// 遍历数据 for in
for (const i in data) {
let type = getDataType(data[i]);
if (type === "Object" || type === "Array") {
container[i] = deepClone(data[i]);
} else {
container[i] = data[i];
}
}
return container;
}
// 待克隆数据
const company = {
name: "Tencent",
position: ["北京", "上海", "深圳"],
leader: {
name: "小马哥",
},
copy: function () {
console.log("与复制了上千种忍术的我为敌,你毫无胜算");
},
};
// 调用函数完成深拷贝
const newCompany = deepClone(company);
newCompany.position[0] = "beijing";
console.log(company);
console.log(newCompany);
// 封装一个函数 用来获取数据类型
function getDataType(data) {
return Object.prototype.toString.call(data).slice(8, -1);
}