JavaScript

296 阅读6分钟

Promise解析

是ES6规范中运用于new Promise()来生成实例,表示异步操作的最终结果的成功或失败。并且有三种状态pending(进行中)、resolved(已成功)、rejected(已失败)。

let promise = new Promise((resolved,rejected) => {
    setTimeout(()=>{
        let number = Math.ceil(Math.random()*100);//1-100随机数
        if (number < 50) {
            resolved('这是小于等于50的数')
        } else {
            rejected('数据超过50了')
        }
    },1000)
});
promise.then((value)=>{
   //resolved的回调
},(err) => {
   //rejected的回调
}).catch((err) => {
    //rejected的回调
})
all的用法和race的用法
function A () {
    let promiseA = new Promise((resolve,reject)=>{})
    return promiseA
}
function B () {
    let promiseB = new Promise((resolve,reject)=>{})
    return promiseB
}
function C () {
    let promiseC = new Promise((resolve,reject)=>{})
    return promiseC
}

let promiseAll = Promise.all([A(),B(),C()])
promiseAll.then((value)=>{
    //三个都成功则成功
},(value)=>{
    //一个失败,则失败
})

let promiseRace = Promise.race([A(),B(),C()])
promiseRace.then((value)=>{
    //谁快执行谁的回调
}).catch((err)=>{
    //谁快执行谁的回调
})
手写简单封装Promise函数
function Promise(exe) {
    let _this = this;//this指向保留
    _this.status = 'pending';//promise的三种状态记录
    _this.success = undefined;//保存成功回调传递值
    _this.fail = undefined;//保存失败回调传递值
    
    _this.onSuccessCallBack = [];//保存成功回调
    _this.onFailCallBack = [];//保存失败回调
    
    function resolve(success) {
        if (_this.status == 'pending') {
            _this.status = 'resolved'
            _this.success = success;
            _this.onSuccessCallBack.forEach(ele => {
                element()
            })
        }
    }
    
    function reject(fail) {
        if (_this.status == 'pending) {
            _this.status = 'rejected';
            _this.fail = fail
            _this.onFailCallBack.forEach(ele => {
                element()
            })
        }
    }
    try {
        exe(resolve,reject)
    } catch (err) {
        reject(err)
    }
}

Promise.prototype.then = function (onResolved,onRejected) {
    let _this = this
    if (_this.status == 'pending') {
        _this.onSuccessCallBack.push(()=>{
            onResolved(_this.success)
        })
        _this,onFailCallBack.push(() => {
            onRejected(_this.fail)
        })
    }
    if (_this.status == 'resolved') {
        onResolved(_this.success);
    }
    if (_this.status == 'rejected') {
        onRejected(_this.fail);
    }
}

this解析

是ES5规范中关于this的指向,以及调用函数的call、apply、bind改变this的指向问题?this永远指向最后调用他的那个对象。this的最顶层的对象就是window(this === window)

1. 箭头函数:创建箭头函数时,箭头函数内的this指向外层的this。
let func = () => {
    console.log(this)  //Window
}
func() 
2. new关键字:当使用new关键字时,函数的this指向新创建的对象。
function fn() {
    consolo.log(this.a)  //123
}
let obj = new fun();
obj.a = 123; 
3. 直接调用:直接调用的函数,函数的this指向全局即为window。
function fn() {
    //严格模式下是undefined
    //非严格模式下是window
    console.log(this);
}
fn();
4. 回调函数:函数的this总是指向window
setTimeout(function () {
    //setTimeout的比较特殊
    //严格模式和非严格模式下都是window
    console.log(this);
});

改变this的指向

使用call、apply、bind函数可以改变this的指向,以及三者的区别。

call、bind、apply例子
let obj = {
    name: "tony",
    function() {
        console.log(this)
        console.log(this.name)
    }
}
let thisArg = {name: 'Joy'}
obj.function.call(thisArg); //Joy
obj.function.apply(thisArg); //Joy
obj.function.bind(thisArg)(); //Joy
apply、call、bind的区别
fun.call(thisArg, param1, param2, ...)
fun.apply(thisArg, [param1,param2,...])
fun.bind(thisArg, param1, param2, ...)
thisArg(可选): fun的this指向thisArg对象。
param1,param2(可选): 传给fun的参数

相同:三者都是改变this的指向,且第一个传递的参数都是this指向的对象 不同:call和bind的传参是以序列的方式传递,而apply传参是以数组的形式传递, call和apply函数是直接执行的,而bind函数是返回一个函数,需调用在执行。

apply、call、bind运用场景
判断数据类型Object.prototype.toString.call() == '[object Object]'
获取数组的最大值最小值Math.max.apply(Math,array)、Math.min.apply(Math,array)
手写实现call
Function.prototype.mycall = function(context,...arr) {
    if (context === null || context === undefined) {
        //指定为null或undefined的this值会自动指向全局对象window
        context = window
    } else {
        // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
        context = Object(context)
    }
    //用于临时存储函数
    let special = Symbol()
    // 函数的this指向隐式绑定到context上
    context[special] = this;
    
    // 通过隐式绑定执行函数并传递参数
    let result = context[special](...array)
    
    // 删除上下文对象的属性
    delete context[special]
    // 返回函数执行结果
    return result
}
手写实现apply
Function.prototype.myapply = function(context) {
    if (context === null || context === undefined) {
        //指定为null或undefined的this值会自动指向全局对象window
        context = window
    } else {
        // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
        context = Object(context)
    }
    //用于临时存储函数
    let special = Symbol()
    // 函数的this指向隐式绑定到context上
    context[special] = this;
    //获取本应传入原函数的参数
    //ES6的扩展运算符可以使arguments这样的类数组转换成数组
    let args = [...arguments].slice(1);    
    //从目的作用域中调用函数并获取返回值
    let result = context[special](args);
    
    // 删除上下文对象的属性
    delete context[special]
    // 返回函数执行结果
    return result
}
手写实现bind
Function.prototype.mybind = function(context) {
    //保存函数本身
    let fun = this
    // 可以支持柯里化传参,保存参数
    let arg = [...arguments].slice(1)
    // 返回一个函数
    return function() {
        //将原参数与返回函数的新参数拼接在一起
        //支持柯里化形式传参
        let newArg = arg.concat([...arguments])
        //返回函数
        return fun.myapply(context,newArg)
    }
}

class解析

是ES6规范中的一个语法糖,其底层是通过构造函数去创建的。(语法糖:是避免编码出错提高效率的一种便携写法)

constructor()

是class类默认的方法,new生成对象实例时自动调用该方法,且clss不能变量提升。

//构造函数
function Person(name,age) {
    this.name = name;
    this.age = age;
}
Person.prototype.introduce = function() {
    return this.name
}
let info = new Person('张三',24)
console.log(info)

//class类
class Person {
    constructor(name,age) {
        this.name = name;
        this.age = age;
    }
    introduce() {
        return this.name
    }
}
let info = new Person('张三',24)
console.log(info) //{name: '张三',age: 24}
静态方法、静态属性
  1. 类相当于实例的原型,所有在类中定义的方法,都会被实例继承。
  2. 但如果在方法前加static关键字,就表示该方法不会被new实例继承,而是直接通过类来调用,这就是"静态方法",父类的静态方法可以被子类继承。
  3. 静态属性是指class本身的属性即Person.sex,而不是定义实例对象(this)上的属性。
class Person {
    static sex = '男'
    constructor() {
        console.log(Person.sex) // 男 
    }
    static classMethod() {
        return 'hello' 
    }
    static getMethods() {
        this.callMethod()
    }
    static callMethod() {
        console.log('call')
    }
    callMethod() {
        console.log('nocall')
    }
}
Person.classMethod() //console.log(Person.classMethod()) //hello
Person.callMethod() //call 如果静态方法包含this关键字,则this指向的是类而不是实例
let info = new Person()
info.classMethod(); //TypeError: info.classMethod is not a function
继承
  1. super关键字,既可以当作对象使用也可以当作函数使用,它表示父类的构造函数,用来新建父类的this对象。
  2. 子类有constructor()方法的必须有super()方法,且可以不写constructor()方法,否则报错。
  3. super在静态方法中指向父类静态方法,在普通方法中指向父类的原型对象.
class Father {}
class Son extends Father {
    constructor(name,age,sex) {
        //调用父类的constructor(name,age)
        super(name,age)
        this.sex = sex
    }
    introduce() {
        //调用父类的introduce()
        return this.sex + super.introduce(); 
    }
}
let getSon = new Son()

//报错继承constructor方法中没有super方法
class Daughter extends Father {
    constructor(){
    }
}
let getDaughter = new Daughter() // ReferenceError

原型和原型链

原型:JS中每一个函数都有一个prototype属性,这个属性指向函数的原型对象,每一个由原型对象派生的子对象,都是相同的属性,子对象就叫构造函数,从实例原型中获取相同属性。 构造函数constructor:每个构造函数都有一个constructor属性,指向该关联的构造函数。 原型链:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针,另一个原型中也包含这一个指向另一个构造函数的指针,层层递进,就构成了实例与原型的链条。