ES5和ES6

总结

ES5

ES5

严格模式

  1. 严格模式
    定义:'use strict'
    范围:
    全局:script标签中的第一句话
    函数:任何一个函数中的第一句话

    后面的语句都默认在严格模式下

    带来了什么作用

    对象中不允许出现重名的属性
    函数中的形式参数不允许重名
    变量不允许重名
    不允许自定义函数中的this指向window,得到结果undefined
    eval() 传入一个字符串 可以针对于JS语句进行解析

Object下的一些方法

  1. Object下的一些方法
  • create方法: 以某一个对象为显式原型属性基础之上建立的新对象

    	let newObj = Object.create(obj,{
    		属性名:{
    			value:100
    		}
    	})
    	newObj.属性名 = 新值;
    
  • defineProperty: Object.defineProperty(哪一个对象,'属性名',{})

  • defineProperties:Object.defineProperty(哪一个对象,{ 属性名:{ value:100 ... } })
    这两个方法都是针对于已有对象来添加属性

关于this指向

  1. 关于this指向:
    默认情况下任何一个函数之中都有this指向
    修改:call、apply、bind
    call和apply:主要在于函数中传递实际参数的语法格式不同
    apply接收参数一定为数组
    哪一个函数.call(要修改的新的this指向的对象,实际参数1,实际参数2....)
    哪一个函数.apply(要修改的新的this指向的对象,[实际参数1,实际参数2....])

bind如果有参数的情况下:
哪一个函数.bind(要修改的那个this指向,实际参数1,实际参数2…)
或者
哪一个函数.bind(要修改的那个this指向)(实际参数1,实际参数2…)

call和bind:
是否立即执行:call修改完this指向,函数将立即执行,bind不会立即执行(后期)
是否有返回值:call没有,bind返回的是与原函数一模一样的新函数对象,又因为bind不会立 即执行,
所以需要手动调用,当然调用的也就是新函数对象

ES6:

ES6:

关键字

  1. 关键字
  • 1.1 let
    let 变量 = 值;
    let 变量名1 = 值, 变量名2 = 值…
    let 变量;

    影响:

    1. 变量不允许重名 let a = 1; let a = 2;
    2. 作用域属于块级
    3. 不存在变量提升
    4. 不影响作用域链
    function f1(){
    	let a = 1;
    	function f2(){
    		//let a = 2;
    		console.log(a)
    	}
    	f2();
    }
    f1();
    
  • 1.2 const
    const 常量名 = 值;
    const 常量名1 = 值, 常量名2 = 值…

    影响:

    1. 变量不允许重名 let a = 1; let a = 2;
    2. 作用域属于块级
    3. 不存在变量提升
    4. 不影响作用域链
    5. const常量值(基础值/地址值)是不允许后期更改

解构赋值 [非常重要]~!

  1. 解构赋值 [非常重要]~!
    数组和对象存在解构赋值

    数组:完全解构
    let [变量1,变量2,变量3] = [1,2,3]

    部分解构
    let [,a,] = [1,2,3]

    对象:完全解构
    let {a,b,c} = {a:100,b:200};

    部分解构
    let {a} = {a:100,b:200}

模板字符串

  1. 模板字符串
  • 3.1 定义 let 变量 = ``;
  • 3.2 使用
    • 3.2.1 可以获取字符串对象中的属性/方法 (正常字符串的使用)
    • 3.2.2 可以换行
    • 3.2.3 可以直接在字符串中解析值(变量(基本数据类型/引用数据类型))
      let 变量 = ${arr[0]}
      let 变量 = ${obj.属性名}
      let 变量 = ${a}

对象的简化方式

  1. 对象的简化方式
    • 4.1 属性的简化:对象中的属性名和外部的变量名一致,且变量名是作为属性值使用的时候,可以简写一个词
    • 4.2 方法的简化:省略了冒号和function
      方法名(){}

箭头函数

  1. 箭头函数
  • 5.1 定义:
    let 变量 = ()=>{}
    方法(()=>{})

  • 5.2 带参数
    只有一个参数,可以省略小括号:let 变量 = 参数名 =>{}

  • 5.3 带返回值
    如果{}中只有一句话,则可以省略{}
    let 变量 = ()=>console.log(xxx)

    如果{}中的语句恰好是箭头函数中的返回值,那么可以省略return
    let 变量 = ()=>值;

  • 5.4 注意事项:

    1. 不能被作为构造函数使用
      2)箭头函数没有自己的this指向,this指向取决于当前函数声明所在的环境(全局环境–window下;函数环境—局部)
    2. 没有arguments,后期可以被rest参数替代

参数默认值

  1. 参数默认值

ES5

function fun(a,b =,c =){
let a = 1;
let b = 2;
let c;
}
fun(1,2)

ES6

let fn = (a,b =,c =)=>{

}
fn(1,2,3)

当如果对应实际参数没有设置,且也没有设置默认值,相当于只是创建了局部变量(undefined)
当如果对应实际参数没有设置,但是设置了默认值,那么形参的内容就按照默认值来
当如果对应实际参数有设置,也设置默认值,则按照实际参数来

参数中完成解构[重要!!]

数组

function f1([a,b,c]){

}
f1([1,2,3])

对象

function f2({x,y =}){

}
f1({x:100,y:200})

rest参数

  1. rest参数
    主要和ES5中的arguments功能类似,当函数传入多少个实际参数的数量不确定的时候,可能会用到rest参数
    rest参数返回的是一个纯数组,
    arguments是一个伪数组,如果想要转换成纯数组,可以使用Array.from(arguments)
    定义:
    let fn = (...args)=>{}
    fn(1,2,3,4)
    

扩展运算符(展开运算符)

  1. 扩展运算符(展开运算符)
    语法:...数组/对象
    展开的是数组/对象的键值中的值(基本值/引用值)
    使用:
    数组:
    let arr = ['a','b','c',{a:1}]
    let arr1 = [...arr];
    
    对象:
    let obj = {x:100,y:200}
    let obj1 = {...obj}	
    

Symbol

  1. Symbol
    • 9.1 定义:let 变量 = Symbol()
    • 9.2 比较:相同内容的Symbol之间比较一定为false,因为Symbol主要是针对于对象中唯一属性名的标识
    • 9.3 使用:
      let 变量 = Symbol('值');
      let obj = {
      [变量]:值(大概率是方法的偏多)
      }
      或者
      let obj = {}
      obj[变量] = 值;

iterator迭代器

  1. iterator迭代器
  • 10.1 哪些数据有迭代器:数组、arguments、set、map、字符串、nodeList、HTMLCollection…
  • 10.2 有迭代器证明可以使用for of循环
    for(let 变量 of 数据){}
  • 10.3 for of和for in之间的区别:
    • for of Es6中提出的
    • for of中的变量存储的值无非下标,for in中的变量存储的就是下标
    • for of中如果想要获取下标需要借助于数组中的entries方法

set集合和map集合

  1. set集合和map集合
  • 11.1 set集合

    • 怎么定义: let 变量 = new Set() 参数不是必要的参数,但是传递的参数接收的是一个一维数组

    • 实际作用:

      • 排重
        let 变量 = new Set([1,3,4,5,6,7,4,2,1,2,3,2])
        • 交集(两个集合中相同的部分)
      	let arr = [1,2,3,4,5,3,5,2,2,4,2]
      	let arr1 = [2,3,4,5,6,2,4,2,4,1,1]
      	let s1 = new Set(arr)
      	let s2 = new Set(arr1);
      	[...s1].filter(item=>s2.has(item))
      
      • 并集(去重后的两个集合放到一起)
      	let arr = [1,2,3,4,5,3,5,2,2,4,2]
      	let arr1 = [2,3,4,5,6,2,4,2,4,1,1]
      	let s1 = [...new Set(arr)]
      	let s2 = [...new Set(arr1)];
      	let arr2 = [...new Set([...s1,...s2])];
      
      • 差集(相对每一个集合中不同的部分)
      	let arr = [1,2,3,4,5,3,5,2,2,4,2]
      	let arr1 = [2,3,4,5,6,2,4,2,4,1,1]
      	let s1 = new Set(arr)
      	let s2 = new Set(arr1);
      	[...s1].filter(item=>!s2.has(item))
      
    • 方法的使用:

      • 添加:s1.add(值)
      • 删除:s1.delete(值)
      • 是否存在:s1.has(值)
      • 清除:s1.clear()
      • 长度:s1.size
      • 遍历:for of
  • 11.2 map集合

    • 怎么定义:let 变量 = new Map() 参数不是必要的参数,但是传递的参数接收的是一个二维数组
    • 实际作用:
      let m1 = [...new Map([['name', '张三'], ['age', 23], ['sex', '男']])];
      	for (let i = 0; i < m1.length; i++) {
      		for (let j = 0; j < m1[i].length; j++) {
      		console.log(m1[i][j])
      	}
      }
    

类和对象

  1. 类和对象
    类:其实更像是一个模板,描述特定类型对象的一个模板
    对象:由这个模板所生产出来的一个实实在在的一个例子,可以操作的

** 定义类**

class 类名{
- 属性的定义
  - 12.1 直接赋值
	属性名 = 属性值(实例化对象身上) [适合于所有实例化对象针对于该属性值是一致的时候]

  - 12.2 通过构造器的方式  
	[适合于所有实例化对象的这些属性的值是不一致的,需要通过调用类的构造函数进行传递实际参数]
	constructor(变量名){
		this.属性名 = 变量名
		this.方法名 = function(){}
	}

- 方法

  - 12.1 直接赋值
 	方法名 = function(){}(实例化对象身上)

	//存入类的原型 
	方法名(){}


	//在类对象本身身上添加属性/方法
	static 属性名  = 属性值;
	static 方法名 = function(){}

	//getter和setter
	
	set 属性名(value){
		this.别名=value;
	}
	get 属性名(){
		return this.别名;
	}
	}
	
	按照这个模板来实例化对象
	let 变量 = new (); // {}
	变量.属性名 = 新值;
-> 类的继承
-> 定义:class 子类 extends 父类{}
-> 继承:子类将父类中的所有的部分全部继承到子类
-> class Person{
constructor(name,age,sex){
	this.name = name;
	this.age = age;
	this.sex = sex;
}
方法名(){}
}
-> class Student extends Person{
	constructor(high){
	super();
	this.high = high;
	}
}
let s1 = new Student(180); // 0x100
console.log(s1.__proto__ = Student.prototype); //均指向一个Object类型的实例化对象  0x200
console.log(Student.prototype.__proto__ = Person.prototype)
console.log(Person.prototype.__proto__ = Object.prototype)

console.log(s1.方法名())

在这里插入图片描述

ECMAScript5语法扩展

一、严格模式

1.1 介绍

ES5 除了正常运行模式,还添加了第二种运行模式:“严格模式”(strict mode)。

严格模式顾名思义,就是使 JavaScript 在更严格的语法条件下运行。

1.2 作用

  1. 消除 JavaScript 语法的一些不合理、不严谨之处,减少一些怪异行为
  2. 消除代码运行的一些不安全之处,保证代码运行的安全
  3. 为未来新版本的 JavaScript 做好铺垫

1.3 使用

全局函数的第一条语句定义为: 'use strict'

如果浏览器不支持,只解析为一条简单的语句, 没有任何副作用

// 全局使用严格模式
'use strict';
girl = '大沫沫';

// 函数中使用严格模式
function main(){
    'use strict';
    girl = '大沫沫';
}
main();

1.4 语法和行为改变

  • 必须用 var 声明变量,不允许使用未声明的变量
  • 禁止自定义的函数中的 this 指向 window
  • 创建 eval 作用域(eval 是一个函数, 接受字符串参数, 会对字符串进行 JS 语法解析并运行)
  • 对象不能有重名的属性(Chrome 已经修复了这个 Bug,IE 出现报错)
  • 函数不能有重复的形参
  • 新增一些保留字, 如: implements interface private protected public

二、Object 扩展方法

2.1 create方法

语法:Object.create(prototype, [descriptors])

Object.create() 详解
Object.create()MDN详解

Object.create 方法可以以指定对象原型(prototype)创建新的对象

同时可以为新的对象设置属性, 并对属性进行描述,但是要注意对象的键值必须是一个对象

已有属性可以随意修改,但是新增的属性必须要通过设置才能有权限操作

如果要是没有以哪一个对象为原型,则可以第一个参数设置为null

  • value : 指定值
  • writable : 标识当前属性值是否是可修改的, 默认为 false
  • configurable:标识当前属性是否可以被删除 默认为 false
  • enumerable:标识当前属性是否能用for in 枚举 默认为 false
  • get: 当获取当前属性时的回调函数
  • set: 当设置当前属性时
//创建一个汽车的对象
var car = {
    name : '汽车',
    run: function(){
        console.log('我可以行驶!!');
    }
};

//以 car 为原型对象创建新对象
var newObj = Object.create(car, {
    brand: {
        value: '奥迪',
        writable: true,         //是否可修改
        configurable: true,     //是否可以删除
        enumerable: true         //是否可以使用 for...in 遍历
    },
    color: {
        value : '黑色',
        wriable: false,
        configurable: false,
        enumerable: true
    },
    price:{
    //如果要是已经使用了getter和setter这种方式,则配置权限属性只能有可以删除和可以枚举两个
        get:function(){
            return this.p;
        },
        set:function(value){
            this.p = value;
        },
        configurable: true,
        enumerable: true
    }
});
console.log(newObj);

2.2 defineProperties

语法:Object.defineProperties(object, descriptors)

直接在一个对象上定义新的属性修改现有属性,并返回该对象

  • object 要操作的对象

  • descriptors 属性描述

    • get 作为该属性的 getter 函数,如果没有 getter 则为undefined。

      函数返回值将被用作属性的值。

    • set 作为属性的 setter 函数,如果没有 setter 则为undefined。

      函数将仅接受参数赋值给该属性的新值。

// 定义对象
var star = {
    firstName: '刘',
    lastName : '德华'
};

// 为 star 定义额外的属性
Object.defineProperties(star, {
    fullName: {
        get: function(){
            return this.firstName + this.lastName;
        },
        set: function(name){
            var res = name.split('-');
            this.firstName = res[0];
            this.lastName = res[1];
        }
    }
});

// 修改 fullName 属性值
star.fullName = '张-学友';

// 打印属性
console.log(star.fullName);

扩展:

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性.

并返回此对象,该方法也是vue实现数据绑定和更新数据的核心代码之一。

区别:defineProperty设置属性需要一个一个的设置,而defineProperties可以批量设置多个属性

语法:Object.defineProperty(object, prop,descriptor)

  • obj:要定义属性的对象;
  • prop:要定义或修改的属性名称,注意属性必须写成字符串
  • descriptor:要定义或修改的属性描述符;

描述对象中的属性及其各自的作用:

属性名作用
configurable标识当前属性是否可以被删除 默认为 false
enumerable标识当前属性是否能用for in 枚举 默认为 false
writable标识当前属性值是否是可修改的, 默认为 false
get获取该属性值时调用该函数
set设置该属性值时调用该函数
value该属性的值或者方法

注意:value属性不能与set和get属性同时使用;writable属性不能与set和get属性同时使用

var obj = {};
//直接在一个对象上操作属性(新增、删除、修改)
Object.defineProperty(obj, 'name', {
    value: '张三',
    writable: true
});

Object.defineProperty(obj, 'age', {
    get: function () {
        return this.a
    },
    set: function (value) {
        this.a = value;
    }
});

Object.defineProperty(obj, 'height', {
    configurable: true,
    writable: false,
    enumerable: true,
    value: 180,
})
obj.name = '李四';
obj.age = 18;
obj.height = 190;
console.log(obj.height);
console.log(obj.age);
console.log(obj);

练习:要求添加 total 属性, 获得班级的总分数

//要求添加 total 属性, 获得班级的总分数
        var banji = {
            name: 'HTML',
            scores: [
                {
                    name: '张三',
                    score: 90
                },
                {
                    name: '李四',
                    score: 85
                },
                {
                    name: '王五',
                    score: 95
                },
                {
                    name: '赵六',
                    score: 88
                }
            ]
        };

        var newObj = Object.defineProperties(banji,{
                total:{
                    get:function(){
                        //定义一个总和变量
                        var sum = 0;
                        //遍历数组
                        for(var i = 0 ; i < banji.scores.length;i++){
                            sum+=banji.scores[i].score;
                        }
                        return sum;
                    }
                }
        });
        console.log(newObj.total);

三、call、apply、bind

  • call 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数
  • apply 方法调用一个具有给定 this 值的函数,以及作为一个数组(或类似数组对象)提供的参数
  • bind 同 call 相似,不过该方法会返回一个新的函数,而不会立即执行

修改this指向:call、apply、bind

哪一个函数.call(要修改的那个this指向,实际参数1,实际参数2…);
哪一个函数.apply(要修改的那个this指向,[实际参数1,实际参数2…]);

 call和apply这两个方法修改完this指向之后,函数将立即执行

bind方法先修改this指向,但是返回了一个与原函数结构一模一样的新函数 ,只不过this指向是新的而已
如果想要让这个函数执行,需要程序员手动调用(新函数)

如果有参数的情况下:
哪一个函数.bind(要修改的那个this指向,实际参数1,实际参数2..)
或者
哪一个函数.bind(要修改的那个this指向)(实际参数1,实际参数2..)
//call apply bind 都是用来改变this的指向
function test(a, b, c) {
    console.log(this)
    console.log(a, b, c)
}
// test(1,2,3);
// call()  参数1:修改后的this, 从参数2开始是实参的列表
test.call({},1,2,3)
// apply()  参数1:修改后的this, 参数2是数组,数组中的元素是实参列表
test.apply({name:'张三'},[1,2,3])

//bind  参数是修改后的this, bind方法执行完返回的是一个与test结构一样的新的函数
// 该函数调用需要传递实参的列表
// var f = test.bind({})
// f(4,5,6)

// console.log(f)//函数
// console.log(f === test)//false

//简写方式
test.bind({})(7, 8, 9)

ECMAScript6语法

一、关键字

let

语法

单变量:let 变量名 = 值;

多变量:let 变量1= 值,变量2=值…,变量n;

//声明一个变量
let a;
console.log(a); //undefined

//定义单变量
let name = "阿香";
console.log(name); //阿香

//定义多变量
let star = "赵丽颖" , age = 33 , arr = [];
console.log(star,age,arr); //赵丽颖 33 []

注意事项

  • 变量不能重复声明
例如:
let num = '34';
let num = 56;
console.log(num); //Identifier 'star' has already been declared
  • let声明的变量属于块级作用域

    常见的块级作用域:if、else、while、for…

    例如:

if(true){
	let star = "天蝎座";
}
console.log(star); //star is not a defined

对比var:

if(true){
	var star = "天蝎座";
}
console.log(star); //天蝎座
  • let声明的变量不存在变量提升

【其实有一些文献中说let存在变量提升,只不过会将变量放在一个暂时性死区中,所以我们无法获取。

​ 所以我们可以直接理解成let声明的变量不存在变量提升】

对比var

console.log(star); //undefined
var star = '天蝎座'

对比let

console.log(star); //报错 Cannot access 'star' before initialization
let star = '天蝎座'
  • 不影响作用域链
function test(){
	let star = '天蝎座';
	function fun(){
		console.log(star); //天蝎座
	}
	fun();
}
test();

const

语法

单常量:const 常量名 = 值;

多常量:const 常量1= 值,常量2=值…,常量n;

注意事项:

  • 常量在声明的时候,一定要赋初始值
例如:
const A; //报错 Missing initializer in const declaration
  • 常量在声明的时候,一般使用全大写的形式(约定)
例如:
const NAME = 'admin';
  • 常量的值,不允许被修改
例如:
const NAME = 'admin';
NAME = 'admin'; //报错 Assignment to constant variable
console.log(NAME);
  • 常量和let变量一样,都拥有块级作用域
例如:
if(true){
	const STAR = '天蝎座';
    console.log(STAR);
}
console.log(STAR);
  • 对于数组和对象元素的修改,不算是对常量的修改,因为数组和对象使用的是地址
const SNAME = ['张三','李四','王二麻子'];
//直接修改不允许
// SNAME = 100;
//但是可以修改数组中的元素
SNAME[0] = '王五';
console.log(SNAME);

const obj = {
    name:'于谦',
    age:40
}
obj.age = 50;
console.log(obj);

二、变量解构赋值

ES6允许按照一定的模式从数组对象提取值,对变量进行逐一赋值,这种过程就被称作解构赋值。

2.1 数组中

语法:let [变量1,变量2…] = 数组

例如:

let arr = ['郭德纲','岳云鹏','老秦','孙越'];
//之前的数组取值:靠下标
console.log(arr[0]); //郭德纲
console.log(arr[1]); //岳云鹏
console.log(arr[2]); //老秦
console.log(arr[3]); //孙越
console.log("=====================");
//现在就可以利用数组的解构赋值
let [guo,yue,qin,sun] = arr;
console.log(guo);//郭德纲
console.log(yue);//岳云鹏
console.log(qin);//老秦
console.log(sun);//孙越


//这是数组解构最基本类型的用法,还可以解构对象数组
let [a, b, c] = [{name: '老王'}, {name: '老李'}, {name: '老刘'}];
console.log(a, b, c); // {name: '老王'}, {name: '老李'}, {name: '老刘'}

//数组模式和赋值模式统一:等号左边和等号右边的形式要统一,如果不统一解构将失败。多维数组解构
let [a, [b, c], d] = [1, [2, 3], 4];
console.log(a, b, c, d); // 1 2 3 4

//提取除第二、三个外的所有数值
let [a, , , d] = [1, 2, 3, 4];
console.log(a, d); //1 4

// 解构中默认值的设置
let [a,b = '我是默认值'] = [1,2]
console.log(a,b);//1,2

//惰性赋值 只有在解构失败时才会允许默认值表达式
let [a,b = '我是默认值'] = [1]
console.log(a,b);//1,我是默认值

2.2 对象中

对象的解构与数组有一个重要的不同。

数组的元素是按次序排列的,变量的取值由它的位置决定;

对象的属性没有次序,变量必须与属性同名,才能取到正确的值。

对象解构赋值的内部机制:是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者

语法:let {变量1,变量2…} = 对象

例如:

//对象
let obj = {
 	name:"张三",
 	age:28,
 	song:function(){
     	console.log('唱');
 	},
 	dance:function(){
     	console.log('跳');
 	}
}
//之前获取对象属性值的方式
console.log(obj.name); //张三
console.log(obj.age); //28
console.log(obj.song); //f(){}
console.log(obj.dance);//f(){}

//现在可以利用对象的解构赋值的方式
let {name,age,song,dance,height,sex = '男'} = obj
console.log(name);//张三
console.log(age);//28
console.log(song);//f(){}
console.log(dance);//f(){}
console.log(height);//解构失败 返回undefined
console.log(sex);//设置默认值 男

//当然并非对象中有多少键值对都需要结构成对应的变量,需要什么解什么
console.log(name);//张三
console.log(song);//f(){}

//当然后期会碰到相对复杂一些的对象结构
let obj1 = {
    name1: "赵丽颖",
    age: 33,
    works: ['知否', '花千骨', '杉杉来了', '楚乔传'],
    hobby: {
        eat: '吃饭',
        sleep: '睡觉'
    }
}
let { name1, age, works: [a], hobby: { eat } } = obj1;
console.log(name1); //赵丽颖
console.log(a); //知否
console.log(eat); //吃饭

三、模板字符串

ES6中引入新的一种声明字符串的方式

  • 语法:字符串

    let str = `我是一种新字符串的定义方式`;
    console.log(str);
    
  • 好处:

    • 内容中可以直接出现换行符

      let str = `
      <ul>
      	<li>张三</li>
      	<li>李四</li>
      </ul>`
      console.log(str);
      
    • 变量间的拼接

      let str = '王五';
      let str1 = `${str}是一个好人`;
      console.log(str1);
      

四、对象简化方式

对象中可以简写属性和方法

当属性名和属性值同名时,可以省略
对象中的函数可以省略function

<body>
    <script>
        let name = "张三";
        let age = 23;
        let getName = function(){
            console.log('获取姓名');
        }
        //声明一个对象
        let obj = {
            // name:"张三",
            // age:23,
            //当属性名和属性值同名时,可以省略
            name,
            age,
            getName,
            /* change:function(){
                console.log('希望通过学习,改变自己');
            } */
            change(){
                console.log('希望通过学习,改变自己');
            }
        }
        console.log(obj); //{name: '张三', age: 23, getName: ƒ, change: ƒ}
    </script>
</body>

五、箭头函数

在ES6中,新增一种新的函数定义方式(箭头函数)

5.1 声明语法
ES5语法
//声明函数
let fn = function(形参1,形参2...){
 	return 返回值;
}
//调用函数
fn(100,200);
ES6语法
//声明函数
let fn = (形参1,形参2...)=>{
 	return 返回值;
}
//调用函数
fn(100,200);
5.2 this指向

箭头函数中的this是静态this,不会随着函数的环境变化而变化;

始终指向函数声明时所在作用域下的this的值,且无法修改this指向

箭头函数中的this与函数前面的调用者无关,与函数声明所在的环境中的this指向有关。

箭头函数本身是没有自己的this指向,其this完全听函数所在的环境(函数内/全局内)中的this

ES5语法
function getName() {
 	console.log(this.name);//王牌对王牌
}
window.name = '王牌对王牌';
const LIST = {
 name: "跑男"
}
//直接调用
getName();
//call方法调用
getName.call(LIST);//跑男
ES6语法
let getName2 = () => {
 	console.log(this.name);//王牌对王牌
}
window.name = '王牌对王牌';
const LIST = {
 name: "跑男"
}
//直接调用
getName2();
//call方法调用
getName2.call(LIST);//王牌对王牌  箭头函数中的this指向不能被修改

let obj = {
    t1:()=>{
        console.log(this);
    }
}
obj.t1();
5.3 不能作为构造函数来实例化对象

构造函数是通过new关键字来生成对象实例,生成对象实例的过程也是通过构造函数给实例绑定this的过程,而箭头函数没有自己的this,创建对象过程,new 首先会创建一个空对象,并将这个空对象的__proto__指向构造函数的prototype,从而继承原型上的方法,但是箭头函数没有prototype,因此不能使用箭头作为构造函数,也就不能通过new操作符来调用箭头函数。

let person = (name,age)=>{
	this.name = name;
	this.age = age;
}
console.log(person);
let p1 = new person();
console.log(p1); //person is not a constructor
5.4 没有自己的arguments变量
let fn = () => {
	console.log(arguments); //Uncaught ReferenceError: arguments is not defined
}
fn(10, 20, 30);
5.5 没有预解析
console.log(fn); //undefined
fn(); //Uncaught TypeError: fn is not a function
var fn = () => {
	
}
fn();
console.log(fn); //Uncaught ReferenceError: Cannot access 'fn' before initialization
fn(); //Uncaught TypeError: fn is not a function
let fn = () => {
	
}
fn();

5.6 简写语法

  • 当有且只有一个形参时,可以省略小括号
let add = n => {
	return n*n;
  }
  console.log(add(10)); //100
  • 当函数体内只有一条语句时候,可以省略花括号
  let add = n => console.log(n);
  add(10); //10
  • 如果有return,必须被省略,函数体执行的结果就是函数的返回值
  let add = n => n*n;
  console.log(add(10)); //100

六、参数初始值

  • 函数中允许形式参数中出现默认值,一般位置都在末尾
function fn(a,b,c=10){
	return a+b+c;
}
let result = fn(100,200);
console.log(result);

let fn1 = ([a, b, c = 5]) => {
    console.log(a, b, c);
}
fn1([1, 2, 3]);

let fn2 = ({ a, b }) => {
    console.log(a, b);
}
fn2({ a: 1, b: 2 })
  • 与解构赋值结合使用,解构赋值的形式先后顺序不影响
function connect({hostname,username,password,port=8080}){
	console.log(hostname);
	console.log(username);
	console.log(password);
	console.log(port); 
}
connect({
	hostname:'localhost',
	username:"root",
	password:"root",
	// port:3306
})

七、rest参数

ES6中新增了rest参数,用于获取函数的实参,用来代替ES5的arguments;

参数名可以自定义,但一般正规args;

rest参数**必须放在形参列表的最后, 输出的都是纯数组 **;

作用:是将实际参数转化成一个数组

主要应用场景是一般不确定实际参数个数的函数上

语法:

function 函数名(…args){

​ …

}

ES5语法

function fun(){
	console.log(arguments);
}
fun('白浅','折颜'); //Arguments(2) ['白浅', '折颜', callee: ƒ, Symbol(Symbol.iterator): ƒ]

ES6语法

function fun(...args){
	console.log(args);
}
fun('白浅','折颜');
/*function fun(...args,a,b) {  //Rest parameter must be last formal parameter
 console.log(args);
}
fun('白浅', '折颜');
*/

function fun(a,b,...args) {
  console.log(a);//10
  console.log(b);//20
  console.log(args);// ['白浅', '折颜']
}
fun(10,20,'白浅', '折颜');

八、spread扩展运算符

ES6语法中新增一种扩展运算符,可以将数组转换成可以逗号分隔的参数序列;

扩展运算符其实也算是rest参数的一种逆运算

区别:

扩展运算符rest参数
是可以把数组展开形成逗号间隔的实际参数序列是将实际参数转化成一个数组

数组展开

扩展运算符相当于是将arr中的元素值复制到arr1里面
如果arr中的元素是基本数据类型,则直接复制 (值复制) 你是你 我是我 后期谁更改与另一方无关不受影响
如果newArr 中的元素是引用数据类型,则复制的是对方的地址值,无论是arr还是newArr 修改了结构,将双方都受影响

let arr = [3,4,5,6,[12,13],{name:"侯志国"}];
let newArr = [...arr];
console.log(newArr);
newArr[2] = 14;
newArr[5].age = 18;
console.log(newArr);//[3, 4, 14, 6, [12, 13], { name: "侯志国",age:18 }]
console.log(arr );//[3, 4, 5, 6, [12, 13], { name: "侯志国",age:18 }]

语法:

function 函数名(){

​ …

}

函数名(…数组名)

//1、声明一个数组
let arr = ['金角大王','霸波奔','银叶先生','独孤求败'];
//2、声明一个箭头函数
let fn1 = (...args)=>{
	console.log(args); //['金角大王', '霸波奔', '银叶先生', '独孤求败']
	//注意:箭头函数内没有arguments
}
fn1(...arr);

对象展开

语法:

let 变量名 = {…对象1,…对象2,…对象n}

其实,间接也是实现了多个对象属性的合并

返回值:新对象

注意:当多个对象都相同的键名时,后者会覆盖前者

//② 对象的展开
let obj1 = {
	name:"苏有朋"
}

let obj2 = {
	name:"赵薇",
	sex:"男"
}

let result = {...obj1,...obj2};
console.log(result); //{name: '苏有朋'}

let obj3 = {
    a:1,
    b:2,
    c:3
}

let obj4 = {
    d:4,
    e:5,
    a:100
}
let obj5 = {...obj3,...obj4}; 
console.log(obj5);//{a:100,b:2,c:3,d:4,e:5}

案例应用1:数组的合并

let arr = ['太上老君','张三丰'];
let arr1 = ['猴砸','曾毅'];
let result = [...arr1,...arr];
console.log(result);//['猴砸', '曾毅', '太上老君', '张三丰']

案例应用1:新数组克隆

let team = ['S','H','E'];
let newTeam = [...team];
console.log(newTeam); //['S', 'H', 'E']
console.log(team == newTeam); //false

案例应用2:将伪数组转为真正的数组

<body>
    <button>按钮1</button>
    <button>按钮2</button>
    <button>按钮3</button>
    <script>
        //将伪数组转化为真正的数组
        let btn = document.getElementsByTagName("button");
        let btns = [...btn];
        console.log(btns); //[button, button, button]
    </script>
</body>

九、Symbol

ES6中引入了一种新的原始数据类型Symbol,表示一种独一无二的值

语法:let 变量名 = Symbol([内容]);

注意:Symbol无法进行运算,拼接等功能

<body>
    <script>
        //创建Symbol
        let s = Symbol();
        console.log(s); //Symbol()
        console.log(typeof s); //symbol

		//但是在实际开发中会给Symbol传递内容,这个内容可以自定义
        //在Symbol中传递内容
        let s1 = Symbol("内容");
        console.log(s1); //Symbol(内容)
    </script>
</body>

Symbol 的值是唯一的

let a = 100;
let b = 100;
console.log(a===b); //true

let s1 = Symbol();
let s2 = Symbol();
console.log(s1===s2); //false

//即使给Symbol中添加相同的内容也是两个不同的Symbol
let s3 = Symbol('html');
let s4 = Symbol('html');
console.log(s3===s4); //false

Symbol值不能与其他数据类型进行计算

let s2 = Symbol("内容1");
console.log(s2+100); //Uncaught TypeError: Cannot convert a Symbol value to a number
console.log(s2+'200');//Uncaught TypeError: Cannot convert a Symbol value to a string

Symbol添加属性

//向对象中添加方法 up down
let game = {
	name:'俄罗斯方块',
};

//给对象添加属性的普通做法
// 上
game.up = function(){
	console.log('上1')
}
game.up = function(){
	console.log('上2')
}
console.log(game)
game.up()
//使用Symbol
let game = {
	name:'俄罗斯方块',
};
//方法1
//给对象创建独一无二的属性
//1.定义一个Symbol
let up = Symbol('up');
let shang = Symbol('up');
//2.给对象添加Symbol的属性
game[up] = function(){
	console.log('上1')
}
game[shang] = function(){
	console.log('上2')
}
//3.调用
game[up]();
game[shang]();

console.log(game)
//方法2
let down = Symbol('xia');
let xia = Symbol('xia');
let game = {
	name: '俄罗斯方块',
	//注意:属性名一定要[],不然就变成了普通的做法了
	[down]: function () {
		console.log('下1')
	},
	[xia]: function () {
		console.log('下2')
	}
};
game[down]()
game[xia]()

console.log(game)

十、iterator迭代器

迭代器(iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。

任何数据结构只要部署iterator接口,就可以完成遍历操作

  • ES6创建了一种新的遍历命令for…of循环,iterator几口主要供for…of循环(相互依赖)
  • 原生具备iterator接口的数据(可以用for of遍历)
    • Array
    • Arguments
    • Set
    • Map
    • String
    • NodeList
    • HTMLCollection
    • TypedArray(用来操作二进制的数据)
//1.数组
var arr = [1,2,3];
console.log(arr);
console.log(arr[Symbol.iterator]);
//遍历
for(let item of arr){
	console.log(item); //item表示数组中的元素值
}

//2.函数arguments
function fun(){
   // console.log(arguments); //Arguments(3) [1, 2, 3... Symbol(Symbol.iterator): ƒ]
   for(var item of arguments){
        console.log(item); // 1 2 3
   }
}
fun(1,2,3);

//3.字符串
var str = new String('hello');
console.log(str);

for(var item of str){
    console.log(item); //h e l l o
}

//4.NodeList
var btns1 = document.querySelectorAll('button');
console.log(btns1); 

for(var item of btns1){
    console.log(item);
}

//5.HTMLCollection
let btns = Array.from(document.getElementsByTagName('button'));
console.log(btns);
//entries() 方法返回一个数组的迭代对象,该对象包含数组的键值对 (key/value)。
//迭代对象中数组的索引值作为 key, 数组元素作为 value。
for(let [item,index] of btns.entries()){
    console.log(item);
    console.log(index);
}

var arr1 = [1,2,3];
for(var [index,item] of arr1.entries()){
      console.log(index);
}

var btns2 = document.getElementsByName('btn');
console.log(btns2); 

for(var [index,item] of btns2.entries()){
    console.log(item);
    console.log(index);
}

var btns3 = document.querySelectorAll('button');
console.log(btns2); 

for(var [index,item] of btns3.entries()){
    console.log(item);
    console.log(index);
}

注意:for of和for in的相同点和不同点:

相同点:都是用来遍历数据的

不同点:

1)for of是ES6中提出的新的遍历方式,只要是数据中有iterator接口,才可以进行for of

2)如果for of 遍历数组,item则是数组中的每一个元素

​ 如果for in遍历数组,item则是数组的每个元素的下标 arr[item] 元素值

3)for of 不能遍历对象,但是for in可以遍历对象

4)for of 本身是无法获取数组下标的,但是可以借助于entries()方法

  • 工作原理
    • 创建一个指针对象,指向当前数据结构的起始位置
    • 第一次调用对象的next方法,指针自动指向数据结构的第一个成员
    • 接下来不断调用next方法,指针一直向后移动,直到指向最后一个成员
    • 每调用next方法返回一个包含value和done属性的对象
      • value表示循环的值
      • done的值默认为false,当迭代完毕时就会显示为true
var arr = ['晓沫','小沫','大沫沫'];
console.log(arr[Symbol.iterator]()); //Array Iterator {}

var str = "hello";
console.log(str[Symbol.iterator]()); //StringIterator {}

//创建一个指针对象
let iterator = arr[Symbol.iterator]();
//调用这个对象下的原型上的next方法
console.log(iterator.next()); //{value: '晓沫', done: false}
console.log(iterator.next()); //{value: '小沫', done: false}
console.log(iterator.next()); //{value: '大沫沫', done: false}
console.log(iterator.next()); //{value: undefined, done: true}
  • 自定义迭代器
let obj = {
	name:'洪家班',
	teams:[
		'洪金宝',
		'林正英',
		'钟发',
		'元彪'
	]
}
//对象中的数组,只要是数组,无论在哪定义只要有迭代器,都可以使用for of迭代遍历
/* for(let item of obj.teams){
console.log(item);
} */
//但是如果想要直接循环obj对象就不行了
//原因是:for of无法直接迭代对象
//但是咱们可以自定义一个迭代器,让其可以循环
let obj = {
	name:'洪家班',
	teams:[
		'洪金宝',
		'林正英',
		'钟发',
		'元彪'
	],
	[Symbol.iterator]:function(){
		//返回值为指针对象(实质就是一个对象)
		//定义一个下标
		let index = 0;
		return {
			next:function(){
			//需要确定value的值
			let data = obj.teams[index];
			//定义一个可以模拟是否迭代完毕的变量,默认值为false
			let done = false;
			//当每一次循环迭代完毕之后,就需要让done的值变成true
			if(index>=obj.teams.length){
				done = true;
			}
			//让下标递增
			index++;
			//返回值:另一个对象,里面包含value和done属性
			return {
				value:data,
				done
			}
		}
	  }
   }
}

//遍历obj对象
for(let item of obj){
	//打印对象里面的值
	console.log(item);
}

注意:在上段代码中函数里面使用的是obj变量,请问能否修改成this,以及修改之后产生的问题?

解决this指向的办法1:将匿名函数修改成箭头函数

 next:()=>{
     //需要确定value的值
     let data = obj.teams[index];
     //定义一个可以模拟是否迭代完毕的变量,默认值为false
     let done = false;
     //当每一次循环迭代完毕之后,就需要让done的值变成true
     if(index>=obj.teams.length){
     	done = true;
     }
     //让下标递增
     index++;
     //返回值:另一个对象,里面包含value和done属性
     return {
     	value:data,
     	done
     }
}

解决this指向的办法2:保存this

[Symbol.iterator]:function(){
    	//返回值为指针对象(实质就是一个对象)
    	//定义一个下标
    	let index = 0;
    	let _this = this;
        return {
            next:function(){
                //需要确定value的值
                let data = _this.teams[index];
                //定义一个可以模拟是否迭代完毕的变量,默认值为false
                let done = false;
                //当每一次循环迭代完毕之后,就需要让done的值变成true
                if(index>=_this.teams.length){
                	done = true;
                }
                //让下标递增
                index++;
                //返回值:另一个对象,里面包含value和done属性
                return {
               	 value:data,
                	done
            	}
          }
    }
}

解决this指向的办法3:修改this指向

return {
    next:function(){
        //需要确定value的值
        let data = this.teams[index];
        //定义一个可以模拟是否迭代完毕的变量,默认值为false
        let done = false;
        //当每一次循环迭代完毕之后,就需要让done的值变成true
        if(index>=this.teams.length){
        	done = true;
        }
        //让下标递增
        index++;
        //返回值:另一个对象,里面包含value和done属性
        return {
       	 	value:data,
        	done
        }
    }.bind(obj)
}

十一、集合之set

一直以来,JS只能使用数组对象来保存多个数据,缺乏像其他语言那样拥有丰富的集合类型。

因此,ES6新增了两种集合类型(set 和 map),用于在不同的场景中发挥作用。

Set是一个不重复值的集合,而且Set只有值没有键

1.1 创建set集合

//创建一个set集合,可以是空集合,也可以是非空集合
let s1 = new Set();
console.log(s1); //Set(0) {size: 0}

//如果是非空集合,那么set中需要传入的参数是可以有迭代器的类型值,不能直接将多个数字作为参数
//例如:字符串、数组
let s2 = new Set([1,2,3]);
console.log(s2); //Set(3) {1, 2, 3}

//数组还可以实现去重的功能
let s3 = new Set([1,4,5,6,5,6,7,6,6,7,5,3,2,5,2]);
console.log(s3); //Set(7) {1, 4, 5, 6, 7, …}

11.2 操作set集合

11.2.1 两个属性
Set.prototype.constructor:构造函数,默认就是Set函数。
Set.prototype.size:返回Set实例的成员总数。
11.2.2 四个操作方法
Set.prototype.add(value):添加某个值,返回 Set 结构本身。
Set.prototype.delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员。
Set.prototype.clear():清除所有成员,没有返回值。
let s1 = new Set([1,2,3,4,5,6]);
        
//元素的个数
// console.log(s1.size);

//添加
// s1.add(7);
// console.log(s1);

//删除
// s1.delete(7);
// console.log(s1);

//检测集合中是否包含某个元素
// console.log(s1.has(4));

//清空
// s1.clear();
// console.log(s1);

//for of遍历
/* for(var item of s1){
	console.log(item);
} */

//扩展运算符
let arr = [...s1];
console.log(arr);
//对比
console.log(s1);

11.3 set集合的应用场景

  • 去除数组重复成员。

    //创建一个数组
    let arr = [4,5,6,7,2,3,4,1,2,3,1];
    //创建一个set集合去重,并在数组中展开
    let newArr = [...new Set(arr)];
    //输出结果
    console.log(newArr); //[4, 5, 6, 7, 2, 3, 1]
    
  • 交集(找出两个set集合中相同的数据部分)

    普通版

    //1.创建两个数组
    let arr1 = [1,1,3,3,4,2,2];
    let arr2 = [3,3,4,4,5,6,7];
    //2.创建两个set集合实现去重(过程1)
    // console.log(new Set(arr1));
    // console.log(new Set(arr2));
    //3.让其中一个集合中去重后的结果展开到真数组中(过程2)
    // console.log([...new Set(arr1)]);        
    //4.在这个数组中过滤值(过程3)
    //[1,3,4,2]
    let newArr = [...new Set(arr1)].filter(function(item){
        //item为arr1去重后的每一个元素
        let s = new Set(arr2);
        // console.log(s); //Set(5) {3, 4, 5, 6, 7}
        //打印s集合中是否包含了item中的值
        // console.log(s.has(item)); //false true true false
        //判断如果包含了item的值则返回true,否则返回false
        if(s.has(item)){
        	return true;
        }else{
        	return false;
        }
    })
    console.log(newArr);  //[3, 4]
    

    升级版

    let s = new Set(arr2);
    let newArr1 = [...new Set(arr1)].filter(item=>s.has(item));
    console.log(newArr1);
    
  • 并集(将两个集合去重后的结果放在一起)

     //1.创建两个数组
     let arr1 = [1,1,3,3,4,2,2];  
     let arr2 = [3,3,4,4,5,6,7];  
     //2.每一个数组去重
     console.log(new Set(arr1));  //{1, 3, 4, 2}
     console.log(new Set(arr2));  //Set(5) {3, 4, 5, 6, 7}
     //3.将两个集合都存入一个真实的数组中,使用扩展运算符
     console.log([...new Set(arr1),...new Set(arr2)]); //[1, 3, 4, 2, 3, 4, 5, 6, 7]
     //4.在将这个数组去重
     console.log(new Set([...new Set(arr1),...new Set(arr2)])); //Set(7) {1, 3, 4, 2, 5, …}
     //5.在将这个集合存入一个真实的数组中
     let newArr = [...new Set([...new Set(arr1),...new Set(arr2)])]; // [1, 3, 4, 2, 5, 6, 7]
     console.log(newArr); //[1, 3, 4, 2, 5, 6, 7]
    
  • 差集(两个集合中相对找不一样的部分)

    基础版

    //1.创建两个数组
    let arr1 = [1,1,3,3,4,2,2];  
    let arr2 = [3,3,4,4,5,6,7];  
    //2.每一个数组去重
    console.log(new Set(arr1));  //{1, 3, 4, 2}
    console.log(new Set(arr2));  //Set(5) {3, 4, 5, 6, 7}
    //3.在第一个集合中过滤出来相对集合2里面不一样的部分
    let newArr = [...new Set(arr1)].filter(function(item){
        //item是arr1集合过滤之后的所有的值
        //  console.log(item);//1 3 4 2
        //在第2个集合中查找相对第1个集合里面不一样的部分
        let s2 = new Set(arr2);
        if(s2.has(item)){
        	return false;
        }else{
        	return true;
        }
        })
    console.log(newArr); //) [1, 2]
    
    let s1 = new Set(arr1);
    let newArr1 = [...new Set(arr2)].filter(function(item){
        return !s1.has(item);
    })
    console.log(newArr1); // [5, 6, 7]
    

    升级版1

    let s2 = new Set(arr2);
    let newArr2 = [...new Set(arr1)].filter(item=>{
    	return !s2.has(item);
    });
    console.log(newArr2);
    

    升级版2

    let s3 = new Set(arr2);
    let newArr3 = [...new Set(arr1)].filter(item=>!s3.has(item));
    console.log(newArr3);
    

十二、集合之map

Map与Set的数据类型相似,他们都是一个集合,但是map是一个键值对集合

Map 数据结构类似于对象,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。

Map方法里面需要传入一个二维数组来形成键值对

12.1 创建map集合

let m0 = new Map();
console.log(m0); //Map(0) {size: 0}
//map参数为二维数组
let m1 = new Map([['name','张三'],['age',18],['address','北京市']]);
console.log(m1); //Map(3) {'name' => '张三', 'age' => 18, 'address' => '北京市'}

12.2 操作map集合

let m0 = new Map();
console.log(m0); //Map(0) {size: 0}
//map参数为二维数组
let m1 = new Map([['name','张三'],['age',18],['address','北京市']]);
console.log(m1); //Map(3) {'name' => '张三', 'age' => 18, 'address' => '北京市'}

//操作map集合
//添加
m1.set('height',180);
console.log(m1); //Map(4) {'name' => '张三', 'age' => 18, 'address' => '北京市', 'height' => 180}

//获取
console.log(m1.get('name')); //张三
console.log(m1.get('age'));  //180
console.log(m1.get('address')); //北京市

let obj1 = {'hobby':['抽烟','喝酒','烫头']};
m1.set(obj1,'于谦')
console.log(m1); //Map(5) {'name' => '张三', 'age' => 18, 'address' => '北京市', 'height' => 180, {…} => '于谦'}
console.log(m1.get(obj1)); //于谦

//删除
m1.delete('address');
console.log(m1); //Map(4) {'name' => '张三', 'age' => 18, 'height' => 180, {…} => '于谦'}

//检测
console.log(m1.has('address')); //false
console.log(m1.has(obj1)); //true

//元素个数
console.log(m1.size); //4

//清空
// m1.clear();
// console.log(m1);

//for of循环
for(let item of m1){
    //是二维数组中的每一个小数组
    console.log(item);
}

十三、类与对象

ES6提供了更接近传统语言的写法,引入了类(class)的概念,作为对象的模板;

通过class关键字,可以定义类,class就可以被看作是一个语法糖;

ES6只是在ES5基础之上让对象原型写法更加清晰,更像面向对象的语法而已;

class中的知识点:

  • class(声明类)

  • constructor(定义构造函数初始化)

  • static(定义静态方法和属性)

  • super(调用父类构造方法)

  • extends(继承父类)

  • 父类方法可以被重写

13.1 定义类

语法:

class 类名{

​ 构造方法(构造器)

​ constructor(){

​ }

​ 方法名(){

​ }

}

注意:

1、构造器名称不可以修改

2、定义的普通方法语法必须使用ES6的语法,不能使用ES5的完整形式

13.2 实例对象

let 对象名 = new 类名([参数1,参数2…])

回顾ES5语法
<body>
    <script>
        //ES5语法:
        function Student(name,age){
            //在对象上添加属性
            this.name = name;
            this.age = age;
        }
        //原型对象上添加方法
        Student.prototype.study = function(){
            console.log('每一个学生都可以学习');
        }
        Student.prototype.play = function(){
            console.log('每一个学生都可以玩');
        }
        //实例化对象
        let s1 = new Student('小明',10);
        console.log(s1); //Student {name: '小明', age: 10}
    </script>
</body>
ES6语法
//首先搞清楚一个概念:函数在类外还叫函数,在类内就叫方法
//1、声明一个类
class Student{
	//4、在实例化对象时,想要对于每一个对象的细节进行区分,需要在类声明的时候,建立对象属性
	//class类中有一个特殊的方法(构造方法),每一个类有且只有一个,方法中的this指向新创建的对象
	//一般情况下,构造函数都会放在类声明的开头,然后是一些其他的方法
	constructor(name,age){
		this.name = name;
		this.age = age;
	}
	//2、声明方法(可以使用ES6方法简化写法)
	study(){
		console.log('每一个学生都可以学习');
	}
	play(){
		console.log('每一个学生都可以玩');
	}
}
//3、实例化对象
//记住:ES5和ES6中只有类声明的方式不同,实例化对象写法是不变的
let s1 = new Student('王二麻子',20);
console.log(s1);//Student {name: '王二麻子', age: 20}

//5、获取对象的属性值
console.log(s1.name); //王二麻子
console.log(s1.age); //20
//6、调用对象下的方法
s1.study();//每一个学生都可以学习
s1.play();//每一个学生都可以玩

13.3 static静态成员

之前添加的属性都是给新创建的对象,如果想要在类身上添加属性,那么这个属性叫静态属性(静态成员)

回顾ES5语法
//ES5语法
//给函数对象添加成员(属性、方法)
//1、声明一个函数对象
function Student(){}
//2、给函数对象添加属性
Student.sname = "测试用户";
//3、给函数对象添加方法
Student.study = function(){
    console.log('每一个学生都特别爱学习');
}
//4、实例化对象
let s1 = new Student();
console.log(s1);//Student {}
console.log(s1.sname); //undefined 由于name和study放在函数身上,并不属于实例化对象,所以无法操作 
//如果想要获取到值,需要从函数身上获取,不能从实例化对象身上
console.log(Student.sname); //测试用户
console.log(Student.study); //f(){}

class 类名{

​ static 属性名 = 属性值;

​ static 方法名(){}

}

//ES6语法
class Student{
    //声明静态成员(属性、方法)
    //属性
    static sname = "测试用户"; 
    //方法
    static study(){
    	console.log('每一个学生都特别爱学习');
    }
}
//实例化对象
let s1 = new Student();
console.log(s1);
console.log(s1.sname); //undefined

//如果想要获取,还是需要找类的成员,不是对象的成员
console.log(Student.sname);//测试用户
console.log(Student.study);//f(){}
Student.study(); //每一个学生都特别爱学习

13.4 对象继承

回顾ES5语法
<body>
    <script>
        //ES5继承
        //人类
        function Person(name,age){
            this.name = name;
            this.age = age;
        }
        //父类原型下的方法
        Person.prototype.eat = function(){
            console.log('民以食为天');
        }
        //学生类
        function Student(num,n,a,s){
            Person.call(this,n,a,s);
            this.sno = num;
        }
        Student.prototype = new Person();
        //修正构造器
        Student.prototype.constructor = Student;
        //子类原型下的方法
        Student.prototype.study = function(){
            console.log('学习可以改变人生');
        }
        let s1 = new Student('小明',10,'001');
        console.log(s1);
        //获取对象下的属性
        console.log(s1.name); // 小明
        console.log(s1.age); //10
        console.log(s1.sno); // 001
        //调用对象的方法
        s1.study(); //本类
        s1.eat(); //父类
    </script>
</body>
ES6语法
<body>
    <script>
        //1、先定义父类
        class Person{   
            //构造方法(并不是简化写法,这是一种固定语法)
            constructor(name,age){
                this.name = name;
                this.age = age;
            }
            //其他方法(可以使用方法的简化写法)
            eat(){
                console.log('民以食为天');
            }
        }

        //2、在定义子类并继承于Person父类
        //继承:class 子类 extends 父类{}
        //当子类继承于父类的时候,构造方法也会同时继承过来
        class Student extends Person{
            //构造方法:注意形参顺序:当子类构造函数自身扩展的参数需要后置,否则顺序就会错乱
            constructor(name,age,num){
                //因为在父类中已经定义过属性,在子类无需再重复定义
                //只需要调用父类的构造方法
                super(name,age);
                //还可以扩展子类自己的属性,this指向的是子类的实例对象
                this.sno = num;
            }
            //子类的方法
            study(){
                console.log('学习可以改变人生');
            }
        }
        let s1 = new Student('小明',10,'001');
        console.log(s1); //Student {name: '小明', age: 10, sno: '001'}
        //调用本类方法
        s1.study(); //学习可以改变人生
        //调用父类方法
        s1.eat(); //民以食为天
    </script>
</body>

13.5 class类的get和set方法

class Phone {
	constructor(brand) {
		this.brand = brand;
	}
	//getter setter 是动态操作对象的属性
	//获取价格
	get price() {
		return this.abc;
	}
	//设置动态的价格
	set price(v) {
		this.abc = v;
	}
}

let huawei = new Phone('华为');

huawei.price = 5000;
console.log(huawei)
console.log(huawei.price)

13.2 对象的深浅拷贝

拷贝就是复制,或者还可以叫克隆,发生在对象数组类型上。

拷贝的核心:通过某种操作对老对象进行复制的操作,并生成了新对象,那么这就是拷贝。

区分浅拷贝和深拷贝:

  • 如果修改了新对象的属性,老对象的属性也修改了,这个时候叫浅拷贝
  • 如果修改了新对象的属性,老对象的属性不修改,这个时候叫深拷贝
13.2.1 浅拷贝
  • 直接赋值的方式
 //数组
 let arr = [1,2,3];
 let arr1 = arr;
 arr1[0] = 10;
 console.log(arr);  // [10, 2, 3]
 console.log(arr1); // [10, 2, 3]
//对象
let obj = {
	name:"张三",
	age:23
}
let obj1 = obj;
obj1.age = 33;
console.log(obj); //{name: '张三', age: 33}
console.log(obj1); //{name: '张三', age: 33}
  • 通过数组中的方法

    • concat
    //数组中的方法
    /*
    	let arr = [1,2,3,4];
    	let newArr = arr.concat([10,20,30]);
    	console.log(arr == newArr); //false
    	arr[0] = 100;
    	console.log(newArr); //[1, 2, 3, 4, 10, 20, 30]
    	console.log(arr); //[100, 2, 3, 4]
    */
    
    //但是要注意:如果原数组中的元素是引用数据类型则修改其中一个,另一个也会受影响
    let arr = [1,{name:'admin'},3,4];
    let newArr = arr.concat();
    console.log(arr == newArr); //false
    arr[1].name = '张三'
    console.log(newArr[1]); //{name: '张三'}
    console.log(arr[1]); //{name: '张三'}
    
    • slice
  let arr = [1,2,3,4];
  let newArr = arr.slice(0);
  console.log(arr);  //[1, 2, 3, 4]
  console.log(newArr); //[1, 2, 3, 4]
  
  arr[0] = 10;
  console.log(arr); //[10, 2, 3, 4]
  console.log(newArr); //[1, 2, 3, 4]
  
  //还是要注意数组中元素为引用数据类型
  let arr1 = [1,[20],3,{a:100}];
  let arr2 = arr1;
  arr2[1] = 30;
  arr1[3].a = 200;
  console.log(arr1); //[1, 30, 3, {…}]
  console.log(arr2); //[1, 30, 3, {…}]
  • 扩展运算符
//扩展运算符
  let arr = [3,{name:'张三'},5,6];
  let newArr = [...arr];
  console.log(arr); //[3, {…}, 5, 6]
  console.log(newArr); //[3, {…}, 5, 6]
  
  //修改新数组的内容
  newArr[1].name = '李四';
  console.log(arr[1]);  //{name: '李四'}
  console.log(newArr[1]); //{name: '李四'}
  • 通过对象Object中的assign方法进行合并
  let obj = {
      name:"老于",
      age:50,
      hobby:['抽烟','喝酒','烫头']
  }
  let newObj = Object.assign({},obj);
  
  
  //修改对象的属性
  newObj.age = 55;
  newObj.hobby.push('说相声');
  newObj.hobby[0] = 'smoke';
  console.log(obj);
  console.log(newObj);
13.2.2 深拷贝

利用JSON的方式

//使用JSON实现深拷贝
let obj = {
	name:'尚硅谷',
	address:['北京','武汉','上海','西安','深圳']
}

//把obj对象转化成JSON字符串
let str = JSON.stringify(obj);
//把JSON字符串 转化成一个新的对象
let newObj = JSON.parse(str);

//修改新对象的属性
newObj.address[0] = 'beijing';

console.log(obj);    //{name: '尚硅谷', address: ['北京','武汉','上海','西安','深圳']}
console.log(newObj); //{name: '尚硅谷', address: ['beijing','武汉','上海','西安','深圳']}

注意:虽然JSON方式实现深拷贝很容易,但是容易有问题【面试中爱问的地方】

let obj = {
    name:"老于",
    age:50,
    hobby:['抽烟','喝酒','烫头'],
    //不能针对方法的拷贝
    test:function(){
    	console.log('测试');
    }
}
let str = JSON.stringify(obj);
console.log(str); //{"name":"老于","age":50,"hobby":["抽烟","喝酒","烫头"]}

let newObj = JSON.parse(str);
console.log(newObj); //{name: '老于', age: 50, hobby: Array(3)}
13.2.2.1 封装函数前的分析
//老对象
let obj = {
    name: 'atguigu',
    pos: ['北京', '上海', '深圳', '武汉', '西安'],
    founder: {
    	name: '刚哥',
    	age: 35
    },
    test: function () {
    	console.log('测试')
    }
}

//分析
//1.准备一个容器(对象 数组)
let newObj = {}
//2.给新对象添加属性 name
newObj.name = obj.name;
//3.给新对象添加属性  pos
// newObj.pos = obj.pos;
newObj.pos = []
newObj.pos[0] = obj.pos[0]
newObj.pos[1] = obj.pos[1]
newObj.pos[2] = obj.pos[2]
newObj.pos[3] = obj.pos[3]
newObj.pos[4] = obj.pos[4]
//4.给新对象添加属性 founder
// newObj.founder = obj.founder;
newObj.founder = {}
newObj.founder.name = obj.founder.name;
newObj.founder.age = obj.founder.age;
//4.给新对象添加方法 test
newObj.test = obj.test.bind(newObj);

// newObj.test = obj.test;

console.log(newObj.test)
console.log(obj.test)
console.log(newObj.test === obj.test)

obj.test();
newObj.test();

function fn(){
    console.log(this);
}
let f = fn.bind();
console.log(f);
console.log(fn);
console.log(f == fn);
13.2.2.2 深拷贝函数的封装

先准备一个对象

//老对象
let obj = {
	name : 'atguigu',
	pos : ['北京','上海','深圳','武汉','西安'],
	founder : {
		name : '刚哥',
		age : 35
	},
	test:function(){
		console.log('测试')
	}
}

在封装一个可以判断所有数据类型的函数

 //封装函数:判断数据类型的
 function getDataType(data) {
 	if (typeof data === 'object') {
 		if (data instanceof Array) {
 			//数组
 			return 'Array';
        } else {
 			//对象
 			return 'Object';
 	}
 	} else if (typeof data === 'function') {
 			//函数
 			return 'Function';
 	} else {
 			//num str bool (基本数据类型)
 			return false;
 	}
 }

//测试数据
let str = '';
console.log(getDataType(str)); //false

let arr = [];
console.log(getDataType(arr)); //Array

let obj = {};
console.log(getDataType(obj)); //Object

let f = function(){}
console.log(getDataType(f)); //Function

最后封装函数

//封装深拷贝函数
function deepClone(oldObj) {
	//1.准备一个容器(对象 数组)
	let wrapper;
	//2.根据老对象判断数据类型,决定容器是 {} 还是 []
	let type = getDataType(oldObj)
	if (type === 'Array') {
		wrapper = [];
	}
	if (type === 'Object') {
        wrapper = {}
	}
	//3.给容器添加新属性
	//枚举老对象的属性
	for (let item in oldObj) {
		//item 是对象的每一个属性
		// wrapper[item] = '123'
		//根据属性值的数据类型去添加
		let typeItem = getDataType(oldObj[item])

		if (typeItem === 'Object' || typeItem === 'Array') {
			//如果是对象或者数组,
			wrapper[item] = deepClone(oldObj[item]);
		} else if (typeItem === 'Function') {
			//如果是函数,通过 bind()去添加
			wrapper[item] = oldObj[item].bind(wrapper)
		} else {
			//如果是普通的数据类型,直接添加
			wrapper[item] = oldObj[item];
		}

}

	//返回值:是新对象
	return wrapper;
}

//新对象
let newObj = deepClone(obj)
console.log(newObj)

十四、数值扩展

		//1.二进制、八进制、十进制、十六进制

        //1) 二进制:以0b开头 ,表示的数字范围在0-1之间

        let num = 0b1101;
        console.log(num); //实际输出的值的进制都是十进制(二进制转化成十进制)  // 13
        /*
         * 1     1    0    1
         * 2^3  2^2  2^1  2^0
         * 8     4    2    1
         * 
         * 1*8 + 1*4 + 0*2 + 1 * 1 = 13
        */

        let num1 = 0b011001;
        console.log(num1); // 25

        /*
         *  0     1    1    0    0    1
         *  2^5  2^4  2^3  2^2  2^1  2^0
         *   32   16   8    4    2     1
         * 
         *  0*32 + 1*16 + 1*8 + 0*4 + 0*2 + 1 * 1
         *   0  +  16 + 8 + 0 + 0 + 1 = 25
        */

        //2) 八进制:以0o开头,表示的数字范围在0-7之间
        let num3 = 0o123;
        console.log(num3);  //八进制转换成十进制  83

        /*
         * 1   2   3
         * 8   8   8
         * 8^2 8^1 8^0
         * 64  8   1
         * 
         * 1*64 + 2*8 + 3*1 = 64 + 16 + 3 = 83
        */

        //3) 十进制: 表示的数字范围在0-9之间
        let num4 = 100;
        console.log(num4);

        //4) 十六进制:表示的数字范围在0-15之间  0-9  a-f
        let num5 = 0xff;
        console.log(num5); //255

        /*
         * 15     15
         * 16     16
         * 16^1  16^0
         * 15 * 16 + 15 =   255
        */
//Number.isFinite  检查一个数字是否是有限数
/**
 * 提示: 
 * 如果 number 是 NaN,或者是正、负无穷大的数,则返回 false。
 * Number.isFinite()与isFinite() 函数不同,isFinite() 会先把检测值转换为 Number ,然后在检测。
 * Number.isFinite() 不会将检测值转换为 Number对象,
 * 如果检测值不是 Number 类型,则返回 false,否则为true
*/


Number.isFinite(123) //true
Number.isFinite(-1.23) //true
Number.isFinite(5-2) //true
Number.isFinite(0) //true
Number.isFinite('123') //false
Number.isFinite('Hello') //false
Number.isFinite('2005/12/12') //false
Number.isFinite(Infinity) //false
Number.isFinite(-Infinity) //false
Number.isFinite(0 / 0) //false
//Number.isNaN 检查一个数值是否为NaN  
//注意:isNaN(window.isNaN)与Number.isNaN的区别
/*
 * isNaN:将参数转换成数字,这个方法是ES5的
 * 		  作用:通过Number方法把参数转换成数字类型,如若转换成功,则返回false,反之返回true
 * Number.isNaN: 不会自行将参数转换成数字, 这个方法是ES6的
 *        作用:用来判断一个值是否严格等于NaN,
 *             它会首先判断传入的值是否为数字类型,如不是,直接返回false。
 *
 * 区别:
 * isNaN方法首先转换类型,而Number.isNaN方法不用;
 * isNaN不能用来判断是否严格等于NaN,Number.isNaN方法可用
*/
console.log(Number.isNaN(NaN));  //true
console.log(Number.isNaN(20));   //false
console.log(Number.isNaN(20 + undefined)); //true
//Number.parseInt  字符串转整数
console.log(Number.parseInt('123456'));  //123456
console.log(Number.parseInt('123456fdsfsfsf')); //123456
console.log(Number.parseInt('fdsfsfsf123456')); //NaN
//Math.trunc 将数字的小数部分抹掉 
console.log(Math.trunc(45.56));  //45
console.log(Math.trunc(10/3));   //3
console.log(Math.trunc(Math.PI)); // 3
//Number.isInteger  判断一个数是否为整数  is[是否] Integer[整型]
console.log(Number.isInteger(10));  //true
console.log(Number.isInteger(10.6)); //false
//Math.pow() 幂运算 
let num = Math.pow(2,3);
console.log(num); //8

let num1 = Math.pow(3,2);
console.log(num1); // 9

//ES7中的写法
let n1 = 2;
let n2 = 3
let result = n1 ** n2;
console.log(result); // 8

十五、对象扩展

15.1 Object.is() 判断两个值是否完全相等

console.log(Object.is(100,100)); //true
console.log(Object.is(100,'100')); //false
console.log(Object.is(NaN,NaN)); //true  【特殊】 只要两个值是一样就认为是相等的
console.log(Object.is(undefined,undefined));//true
console.log(Object.is(undefined,NaN)); //false
console.log(Object.is(undefined,null)); //false

console.log(NaN === NaN); //false
console.log(undefined == NaN); //false
console.log(undefined === NaN); //false
console.log(undefined === null); //false
console.log(undefined == null); //true undefined派生自null

15.2 Object.assign() 对象的合并

//Object.assign 对象的合并
let obj1 = {
	a:1,
	b:2
}

let obj2 = {
	c:3,
	d:4,
	a:5
}
//合并的原理:后者对象向前者对象合并,当如果两个对象合并的时候出现了相同的键名,则是后者对象的键值
let newObj = Object.assign(obj1,obj2);
console.log(newObj);

console.log(obj1);
console.log(obj2);

console.log(obj1 == newObj); //true
console.log(obj2 == newObj); //false

//扩展运算符
let newObj1 = {...obj1,...obj2};  //{a:5,b:2,c:3,d:4}
console.log(newObj1);

console.log(newObj1 == obj1);  //false
console.log(newObj1 == obj2);  //false

15.3 直接修改 proto 设置原型

let obj1 = {
	a:1,
	b:2
}
let obj2 = {
	c:3,
	d:4
}
obj1.__proto__ = obj2;
console.log(obj1);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值