js进阶高级与ES6,常用函数方法

  • Post author:
  • Post category:其他




ES6与高级

2021.9.11



class类的使用

类的本质就是function函数

在ES6中声明类用class关键字

  1. ES6中类没有变量提升,所以必须先定义类,才能实例化对象
  2. 类里面的共有属性和方法在内部调用,一定要加this使用
  3. constructor构造函数的this指向实例对象,方法里的this指向方法调用者(下面有案例)

创建类

//创建一个类的格式
class 类名 {
    //constructor是构造函数,即使不写,也会自动创建
	constructor(user,age){
        //共用的属性放这里
		this.user = user;	//this必须加,指向的是创建的实例对象
		this.age = age
	}
    //类中的方法
	方法1(){} 	//实例对象访问
	方法2(){}
    // 静态属性,只能由构造函数访问(类名)
    lang = "abc"
    // 静态方法
    static 方法3(){}
}
//调用类
var ldh = new 类名('李得还',18);
ldh.方法1();
类名.方法3();

继承类

class Father {}	//父类
class Son extends Father{}	//子类,extends为关键字,代表Son继承Father

super关键字调用父类的函数,构造函数

//父类
class Father {
	say(){
		return '父类'
	}
}
//子类继承父类
class Son extends Father {
    say(){
        return super.say();	//调用父类的方法,将super当对象调用
    }
}
//继承中,优先执行子类的方法,先看子类是否有该方法,如果没有则去执行父类中的该方法

super关键字调用父类的构造函数

//父类
class Father {
    constructor(x,y){
		this.x = x;
        this.y = y;
    }
    sum(){
    	console.log(this.x + this.y);
    }
}
//子类继承父类
class Son extends Father {
	constructor(x,y){
		super(x,y)	//,参数传入父类构造函数,调用了父类中的构造函数,将super当方法用
	}
    say(){
        return super.say();	//调用父类的方法,将super当对象调用
    }
}

子类继承父类的方法,同时拥有自己的方法

//父类
class Father {
    constructor(x,y){
		this.x = x;
        this.y = y;
    }
    sum(){
    	console.log(this.x + this.y);//必须加this.
    }
}
//子类继承父类
class Son extends Father {
	constructor(x,y){
        //利用super 调用父类的构造函数
		super(x,y);	//super 必须先调用父类构造方法,在使用子类构造方法
        //相当于 先有父类,在有子类
        this.x = x;
        this.y = y;
	}
    subtract(){
        console.log(this.x - this.y)
    }
}

constructor构造函数的this指向实例对象,方法里的this指向方法调用者

<button>点击</button>
<script>
    class Father {
        constructor(user) {
            this.user = user;
            this.btn = document.querySelector('button');
            this.btn.onclick = this.sing;//绑定点击事件
        }
        sing(){
            //当点击按钮后,这里的this指向的是btn按钮
            console.log(this);
            console.log(this.user); //btn按钮中没有该属性,所以会报错,无法输出abc
        }
         dance(){
             //这里的this指向的是实例对象 ldh,因为是ldh调用了这个方法
            console.log(this.user);
        }
    }
    var ldh = new Father('abc');
    ldh.dance()
</script>



原型对象


构造函数原型 prototype

构造函数通过分配的函数是所有对象所共享的

js规定,每一个构造函数都有一个prototype属性,指向另一个对象,这个prototype就是个对象,这个对象的所有属性和方法都会被构造函数拥有

可以把那些不变的方法,直接定义在prototype对象上,这样所有对象的实例都可以共享这些方法,也节省了内存空间

function Star(uname,age){
    this.uname = uname;
    this.age = age;
    //this.sing = function(){ console.log("哈哈哈") }
}
//将方法放到prototype对象上,节省空间
Star.prototype.sing = function(){  console.log("哈哈哈123") }
//初始化实例对象
let ldh = new Star('ddd', 18);
let adh = new Star('aaa', 18);
console.log(ldh.sing == adh.sing);	//true,比较的是地址
console.log(Star.prototype); //构造函数对象
console.log(ldh.__proto__); //实例化对象


实例对象原型proto

实例化的对象都会有一个__proto__对象,对象原型对象__proto__指向构造函数的prototype原型对象
意义在于为对象的查找机制提供一个方向,实际开发中不使用这个属性(目前浏览器已不显示)

静态成员:在构造函数上添加的成员为静态成员,只能由构造函数本省来访问

实例成员:在构造函数内部创建的成员为实例成员,只能由实例化的对象来访问

js的成员查找机制

1.当访问一个对象属性(方法)时,先查找对象自身是否有该属性
2.如果没有就查找它的原型,也就是__proto__指向的prototype原型对象


constructor构造函数

对象原型(__proto__)和构造函数(prototype)原型对象里面都有个属性constructor属性,称为构造函数,因为它指向构造函数本身
constructor主要记录对象引用于哪个构造函数,可以让原型对象指向原来的构造函数
function Star(uname, age) {
	this.uname = uname;
	this.age = age;
}
//将方法放到prototype对象上
Star.prototype.sing = function () {
	console.log("哈哈哈123" + this.uname);
}
var ldh = new Star('ddd', 18);
console.log(Star.prototype.constructor); //输出的就是构造函数
console.log(ldh.__proto__.constructor); //输出的就是构造函数

有些情况下,需要手动将constructor属性指向原来的构造函数

//将方法放到prototype对象上
//对象的写法,相当于覆盖了原先的prototype对象
Star.prototype = {
    //覆盖了,所以需要重新指向原来的构造函数
	constructor: Star,
    // 声明的静态方法
	sing: function(){
		console.log("唱歌");
	},
	movie: function(){
		console.log("演电影");
	}
}


构造函数,实例,原型对象三者之间的关系

Star构造函数 》 Star原型对象prototype 》 Star构造函数
Star构造函数 》 ldh对象实例 》 Star原型对象prototype 》 Star构造函数


原型链

成员查找机制,实现继承

实例对象__proto__指向
Star构造函数 》 ldh对象实例 》 Star原型对象prototype 》 Object原型对象 》 null



扩展内置方法

通过原型对象的原理,可以给内置对象增加自定义方法

// 给Array对象添加个求和方法

//给Array内置对象添加求和方法
Array.prototype.sum = function () {
    let sum = 0;
    for (let i = 0; i < this.length; i++) {
        sum += this[i];
    }
    return sum;
}
// 调用添加的求和方法
let arr = [10, 20, 30];
console.log(arr.sum()); //60

内置的方法都存在原型对象上



继承

Call可以调用方法,也可以改变this的指向

function fn(){
    console.log("哈哈哈");
    console.log(this);	//第一次window, 第二次o
}
fn.call();	// 调用方法
//声明对象
let o = {
    name: 'andy'
}
//第一个参数就是要this指向的 对象
fn.call(o);	 //call可以改变this指向

借用构造函数继承父类型属性

 // 父构造函数
function Father(uname,age){
	this.uname = uname;
	this.age = age;
}
// 子构造函数
function Son(uname,age){
	 // this 指向子构造函数的对象实例
	 Father.call(this,uname,age);	//调用父构造函数,并将父构造函数的this指向Son,并将两个属性传递进去,此时Son已经拥有这两个属性了,也就是继承
}
var son = new Son('ldh',18);
console.log(son)

借用构造函数继承父类型方法(原型继承)

// 父构造函数
function Father(uname, age) {
    this.uname = uname;
    this.age = age;
}
//添加到原型上的方法
Father.prototype.money = function () {
    console.log(10000);
}
// 子构造函数
function Son(uname, age) {
    // this 指向子构造函数的对象实例
    Father.call(this, uname, age);	//调用父构造函数,并将父构造函数的this指向Son,并将两个属性传递进去,此时Son已经拥有这两个属性了,也就是继承
}
// 继承父构造函数原型对象的方法
// 将子原型对象指向 父元素原型对象,这样可以继承到父原型对象的方法,但是会有问题,子会影响父
// Son.prototype = Father.prototype;
// 解决方法,
// 创建一个实例对象,赋值给Son原型对象,这样就继承了父原型对象的方法
// 因为是一个新的实例对象,所以不会影响父原型对象
Son.prototype = new Father();
// 如果利用对象的形式修改覆盖了原型对象,需要用constructor指回自己构造函数
Son.prototype.constructor = Son;
Son.prototype.exam = function () {
    console.log("子元素考试");
}
var son = new Son('ldh', 18);
console.log(son)
console.log(Father.prototype);
console.log(Son.prototype.constructor);



预解析

代码先解析

var str;	//声明变量
function add(){};	//声明函数

只声明,不会赋值

var sud = function(){};	//这种属于变量,只会预解析变量声明,后面的函数并不会解析

预解析优先级

//先解析的函数
console.log(a);	//输出 函数体a
function a (){
	console.log("aa")
}
var a = 1;	//此时1值将函数a给覆盖了,所以此时a=1
console.log(a);	//输出 变量a的值  1

let的预解析

let 定义的变量
1.也会预解析,但是必须先初始化赋值,才能使用(其它语言也是遵循此规则)
var则 可以先使用,在初始化



解构赋值

ES6中允许从数组中提取值,按对应位置,对变量赋值,对象也可以解构

数组解构

let arrs = [12, 52, 32]
let [a1, a2, a3] = arrs;  //从数组按顺序提取数据存到变量
console.log(a1, a2, a3);	//输出 12 52 32
// 按需取值
let [a,,b] = arrs;	//用逗号占位即可
console.log(a,b);	//a=12,b=32

多维数组

let arrs = ["A", "B", "C", ["A1", "A2", "A3"]];
let [,a,,[,b]] = arrs;
console.log(a,b);	a 是"A" ,b是 "A2"

对象解构

let person = {name: "小丁", age: 18};

let { name, age } = person; //方式1
console.log(name, age);

let { name: mname } = person;   //方式2
console.log(mname);

对象多层结构

let obj = {
	uname:"哈哈哈",
	dog: {
		uname: "aaa"
	}
}
let {dog:{uname}} = obj;	取对象中的对象中的uname



剩余参数与扩展运算符

ES6中的剩余参数和扩展运算符


剩余参数

区别:arguments是一个伪数组,剩余参数是一个真数组(Array),具有全部方法

但是在箭头函数中没有arguments,只能用剩余参数,就是3个点

...

剩余参数语法允许将一个不定数量的参数表示为一个数组

function sum(first, ...args) {
    console.log(first); // 10
    console.log(args);  // [20, 30] 数组形式
}
sum(10, 20, 30);

剩余参数和解构配合使用

let students = ['wangwu', 'zhangsan', 'lisi'];
let [s1, ...s2] = students;
console.log(s1); // 'wangwu'
console.log(s2); // ['zhangsan', 'lisi']


扩展运算符

将数组转为用逗号分割的参数序列

let ary = [1, 2, 3];
...ary // 用逗号分割的结果是 1, 2, 3
console.log(...ary); // 1 2 3	这里逗号相当于console.log(1, 2, 3)

用于合并数组

// 方法一
let ary1 = [1, 2, 3];
let ary2 = [3, 4, 5];
let ary3 = [...ary1, ...ary2];	//合并
// 方法二
ary1.push(...ary2);	//添加方式合并

将伪数组转真数组

let divs = document.getElementsByTagName('div');
divs = [...divs];
//利用数组方法转换
let arr = Array.from(divs);



Set 数据结构

它类似于数组,但是成员的值都是唯一的,没有重复的值

// 初始化实例对象
var s = new Set();
var s = new Set([2,3,4,5,4]);	// 也可以接收数组作为参数来初始化
console.log(s); //{2, 3, 4, 5}  重复的4没有了

实例方法

add(value) 添加某个数据,返回Set结构本身

delete(value) 删除某个数据,返回布尔值,表示删除是否成功

has(value) 查询数据,存在返回true

clear() 清除结构中所有成员

遍历,与数组一样,拥有forEach方法

s.forEach(value => console.log(value))



创建对象

uname = "aaa";
age = 18;
let obj = {
	uname,	//在ES6中 如果属性名和变量名一样,则可以省略属性名
	age
}



ES5新增方法



数组方法

迭代方法:forEach()、map()、filter()、some()、every(),find(),findIndex()

arr.forEach 迭代(遍历)数组

// arr.forEach(回调函数)
var arr = [1, 2, 3];
arr.forEach(function (value, index, array) {
	console.log('每个数组元素 ' + value);
	console.log('每个数组元素索引 ' + index);
	cosole.log('数组本身 ' + array)
})

arr.filter 筛选数组

// arr.filter(回调函数)
var arr = [12, 77, 56, 1, 8, 20];
var newArr = arr.filter(function (value, index, array) {
    return value >= 20;//找出大于20的数字
})
console.log(newArr); // 返回数组,包含所有满足条件的元素

arr.some 查找是否有满足条件的元素

var arr = [12, 35, 9];
var flag = arr.some(function (value, index) { return value == 35; }) //如果找到了则 立即停止迭代
console.log(flag); // true ,如果找到了则返回true

其它迭代方法

arr.find();	//遍历数组,查找满足条件的第一个成员并返回,不存在则返回undefined
arr.findIndex()//遍历数组,查找满足条件的第一个成员,并返回该索引值,不存在则返回-1
arr.some()	//	查找满足条件的成员,如果找到 则立即停止遍历,然后返回true
arr.every() // 查找满足条件的元素,所有元素都满足 返回true,

map

// 遍历数组,给每个成员处理,然后得到处理后的新数组
let arr = [1, 3, 2, 4, 5, 6, 7, 8]
let newArr= arr.map(function (value, index) {
    return value * 2; //每个元素乘2
})
console.log(newArr); // [2, 6, 4, 8, 10, 12, 14, 16]

reduce

//reduce()遍历数组,将结果汇总为单个返回值
arr.reduce(回调函数,[初始值])	//未提供初值则会跳过第0次循环,从1开始
//回调函数有4个参数
accumulator(累计器acc) 第一次遍历 该值是初始值
currentValue(当前值cur)
[index](当前值索引)
[array](源数组)

//能实现的功能
取字符出现次数,数组去重,多维转1维

Array.from() 遍历数组,可将伪数组转换真数组

let arr = {
    "0": 1,
    "1": 2,
    "length": 2
}
let newarr = Array.from(arr, item => tiem)

方法

includes()方法,查找数组是否包含某给值,返回布尔值

var arr = ["abc", "dd", "ee"];
arr.includes("dd"); // true



对象方法

Object.values() 获取所以属性值

Object.keys() 用于获取对象自身的所有属性名

var obj = {
    id: 1,
    pname: '小米',
    price: 1999
}
var arr = Object.keys(obj); //得到属性名的数组
console.log(arr); //["id", "pname", "price"]

Object.defineProperties 定义新属性或者修改原有属性

Object.defineProperties(obj对象,prop属性名,descriptor对象形式参数)

// 定义新属性 或修改原有的属性
Object.defineProperties(obj, 'num', {
    value: 9.9, //定义或修改原属性值
    writable: false, //值是否可重写true 默认false
    enumerable: false, //属性是否可被枚举(遍历) true 默认false
    configurable: false //属性是否可被删除或修改特性 true 默认false
})



函数

所有函数都是Function的实例(对象)

判断的对象 instanceof 用于比较的对象

定义函数的方式

// 带名字的函数
function fn(){};
// 匿名函数
let fn = function(){};
//实例化函数写法
let fn = new Function('参数1','参数2','函数体');
// 例如
let fun = new Function('a','b','console.log(a + b)');
fun(1,2);  //调用



函数形参默认值

// 函数的参数的默认值,在没有传参的时候,会使用默认值
function getInfo(a=0,b=2){
	return a + b;
}
getInfo(undefined,2);	//第一个参数留空要写 undefined填充,



函数内this指向

调用方式 this指向
普通函数调用 window
构造函数调用 实例化的对象,原型对象里的方法也指向实例对象
对象方法调用 该方法所属对象
事件绑定方法 绑定事件的对象
定时器函数 window
立即执行函数 window


改变函数内this指向

三种方法,call() apply() bind()

Call改变this的指向,常用语继承

function fn(){
    console.log("哈哈哈");
    console.log(this);	//第一次window, 第二次o
}
fn.call();	// 调用方法
//声明对象
let o = {
    name: 'andy'
}
//第一个参数就是要this指向的 对象
fn.call(o);	 //call可以改变this指向

apply改变this的指向

// 用法 fun.apply(thisArg指向的对象,[argsArray]伪数组)
var o = { name: 'andy' };
function fn (arr){ console.log(this);console.log(arr) };
fn.apply(o,['pink']); //伪数组
//可用于Math.Max()传递数组来取最大值
//自己使用需要arguments来获取数组每个元素

bind 改变this的指向

但是不调用函数,返回改变this之后的新函数

可用于回调函数,方便指向this

var o = { name: 'andy' };
function fn (arr){ console.log(this);console.log(arr) };
var f = fn.bind(o);	//不会调用,返回改变this之后的新函数
f();
改变函数内this指向,但是不会立即执行
var btn = document.querySelector('button');
btn.onclick = function(){
	this.disabled = true; 点击后禁止按钮
	// 启动临时定时器,3秒后执行
	setTimeout(function(){
		this.disabled = false;//3秒后解开按钮
	}.bind(btn),3000)  // bind改变函数内的this指向
}



箭头函数

声明函数

//正常的写法
function sum(s1, s2) { return s1 + s2 };
//箭头函数写法
const sum = (s1, s2) => { return s1 + s2 };

//简写,当函数体只有一句代码,且执行结果就是返回值,可以省略大括号,和return
const sum = (s1, s2) => s1 + s2;
function fn(a) { return a; }
//如果形参只有一个,可以省略小括号
const fn = a => a;
fn(12);

箭头函数不绑定this关键字,箭头函数中的this指向 函数定义位置的上级作用域this

也不能用call修改this

//作用域this
let obj = {
    age: 18,
    say: () => {
        alert(this.age); // 输出undefined
        // 因为上级作用域是window,window下没有age变量
    },
    hel: function(){
        console.log(this) //输出obj,调用者
    }
}
obj.say();
obj.hel();

箭头函数中的this指向位置

function get() {
	say = () => {
		console.log(this);	//输出 window对象
		// 上级作用域是get(),get()的this是window
	}
	say();
}
get();



严格模式

代表在严格的条件下运行JS代码

消除了js语法的一些不合理,不严谨之处,和不安全之处,提高编译器效率

禁用了一些未来可能出现的语法关键字比如,class,enum,export,extends,import,super 不能用做变量名

变量必须声明 才能使用,不能使用delete删除已经定义的变量

开启严格模式

<script>
	'use strict'
	//后面的代码都将执行严格模式
</script>

开启严格模式-给函数开启

function(){
	'use strict'
	//这里面的代码将执行严格模式
}

严格指向的问题

1.以前在全局作用域函数中的 this 指向 window 对象。
2.严格模式下全局作用域中函数中的 this 是 undefined。
3.以前构造函数时不加 new也可以 调用,当普通函数,this 指向全局对象
4.严格模式下,如果 构造函数不加new调用, this 指向的是undefined 如果给他赋值则 会报错
5.new 实例化的构造函数指向创建的对象实例。
6.定时器 this 还是指向 window 。
7.事件、对象还是指向调用者。

8.不允许在if for 里面声明函数



高阶函数

高阶函数是 对其它函数进行操作的函数

参数形参是函数的就是高阶函数,返回值是函数的也属于高阶函数

function(fn){	//形参里有函数,高阶函数的特征
	fn();
}
function fn(){
	console.log("123");
}



闭包

衍伸了变量的作用范围,可以在外面访问内部作用域的局部变量

Scope 里面会有两个参数(global 全局作用域、local 局部作用域)。浏览器的Scope里面多一个Closure参数,这就说明产生了闭包

function fn(){
	var num = 10;
	return function(){ console.log(num) };
}
var f = fn();
f()	执行了局部作用域的函数 和变量
// 闭包的简单例子,2秒后打印所以的li标签
var li = document.querySelectorAll("li");
for (var i = 0; i < li.length; i++) {
    // i是变动的,等到事件执行的时候i已经变成4了,所以需要闭包
    // 创建一个立即执行函数,将i传递进去,此时a变量会一直保存到事件执行完毕
    (function(a){
        setTimeout(function () {
        console.log(li[a].innerHTML);
    }, 2000)
    })(i)
}



递归

函数调用自身来达到循环执行的目的

var i = 0;
function prote(){
    i++;
    console.log(i); // 会输出6次,然后停止
    if(i>=6) return;
    prote()
}
prote();



浅拷贝深拷贝

浅拷贝=只拷贝第一层数据,深层次拷贝的是引用地址,深拷贝=复制数据到新的内存空间地址

Object.assign() 拷贝属于浅拷贝,不能拷贝深层次的对象数据。如果属性名相同会覆盖

Object.assign(存放的对象,要拷贝的对象);
// 深拷贝对象(保存的变量,待拷贝对象)
function lstrcpy(paste, oringe) {
    for (const k in oringe) {
    	// 必须先判断数组,因为数组也是对象
        if (oringe[k] instanceof Array) {
            paste[k] = []; //设置成空数组
            lstrcpy(paste[k], oringe[k])
        } else if (oringe[k] instanceof Object) {
            paste[k] = {}; // 设置成空对象
            lstrcpy(paste[k], oringe[k])
        } else {
            paste[k] = oringe[k];
        }
    }
}
var obj = JSON.parse(JSON.stringify(待拷贝对象)); //转换方式深拷贝



异常捕获处理

用于处理运行中可能会发生错误的代码,进行捕获处理后 程序就不会中断 就能继续往下执行

try…catch异常捕获

//接管代码错误处理
try {
    // 用try包裹可能发生错误的代码
    p = document.querySelectorAll("p");
    p.style.color = "red";
} catch (error) {
    // 用catch写当发生错误时要如何处理
    console.log(error, error.message); // 输出错误信息
}
// 即使前面代码抛出异常了,由于处理了异常,所以程序还能正常执行
document.querySelector("p").style.backgroundColor = "blue"

throw抛出错误,Error错误对象

function getinfo(x, y) {
    if (x == undefined || y == undefined) {
        throw new Error("getinfo参数不能为空"); //抛出错误
    }
    return x + y;
}
// throw抛出异常后,程序将会停止执行
//new Error("错误提示信息"); 错误对象



禁止调试代码

setInterval(function () {
    check()
}, 4000);

var check = function () {
    function doCheck(a) {
        if (("" + a / a)["length"] !== 1 || a % 20 === 0) {
            (function () { }
            ["constructor"]("debugger")())

        } else {
            (function () { }
            ["constructor"]("debugger")())

        }
        doCheck(++a)
    }
    try {
        doCheck(0)
    } catch (err) { }
};
check();



版权声明:本文为weixin_46028831原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。