• JavaScript重难点


    一、call、apply、bind(改变函数内部this指向)

    使用场景:一般绑定方法的时候使用bind;在调用其他实例方法时候多用call/apply

    1.call的作用

    ①.可以调用函数

    var obj = {
        name:'alhh'
    }
    function fn(){
    console.log(this)
    }
    fn.call() //window

    ②.改变this指向

    var obj = {
        name:'alhh'
    }
    function fn(){
    console.log(this)
    }
    fn.call(obj) //{name:'alhh'}

    应用:主要作用用来实现继承

    function Father(name,age){
      this.name = name
      this.age = age  
    }
    function Son(name,age,sex){
        Father.call(this,name,age,sex)
        this.sex = '男'
    }
    
    var son = new Son('al','30')
    console.log(son) // {name: "al", age: "30", sex: "男"}

    手写call原理:

    Function.prototype.myCall = function (context){
      if(typeof this !=='function'){
        throw new TypeError('Error')
      }
      var context = context || window; //第一个参数为调用call方法的函数中的this指向
      context.fn = this; //将this赋值给context的fn属性 此处this指的是调用myCall的function
      const args = [...arguments].slice(1)
      const result = context.fn(...args); //去除参数的第一个值后执行这个添加的函数
      delete context.fn; //删除这个属性
      return result;
    };

    2.apply作用

    ①.可以调用函数

    ②.可以改变this的指向

    var obj = {
    name:'alhh'
    }
    function fn(){
    console.log(this)
    }
    fn.apply(obj) //{name:'alhh'}

    与call不同的是,传递的参数是数组形式或者伪数组

    var obj ={
    name:'alhh'
    }
    function fn(args){
    console.log(this) //{name:'alhh'}
    console.log(args) //字符串形式hello
    }
    
    fn.apply(obj,['hello'])

    应用:求数组中的最大值

    var arr =[1,33,444,55,666]
    //如果不需要改变this指向第一个参数可以为null
    var max =Math.max.apply(null,arr) 或者 //Math.max.apply(Math,arr)
    console.log(max) //666

    手写apply原理:和call类似主要处理参数

    Function.prototype.myApply = function (context,args){
     if(typeof this !=='function'){
        throw new TypeError('error')
    }    
        var context = context || window
        context.fn = this
        const result = context.fn(..args)
        delete context.fn
        return result
    }

    3.bind作用

    与call和apply不同的是 bind不会调用函数,返回的是由指定的this值和初始化参数改造的原函数的拷贝

    相同的是可以改变this的指向;第一个参数是this指向,后面的参数和call相同 不是数组

    var obj = {
    name:'alhh'
    }
    function fn(){
    console.log(this)
    }
    var newFn =fn.bind(obj)
    newFn() //{name: "alhh"}

     应用:如果有的函数不需要立即调用,但是又想改变这个函数内部的this指向 此时使用this

     二、new的实现

    首先需要知道new的时候做了什么事情

    1.创建一个新的对象

    2.将构造函数的作用域赋给新对象(因此this就指向了这个新对象)

    3.执行构造函数里面的代码(为这个新对象添加属性)

    4.返回新对象

    function myNew(){
    //创建一个新的实例对象
     var obj = new Object()
    //取得外部传入的构造器
     var Constructor = Array.prototype.shift.call(arguments)
    //实现继承,实例可以访问构造器的属性
     obj.__proto__ = Constructor.prototype
    //调用构造器,并改变this指向到实例 (参数是数组,要用apply)
     var res = Constructor.apply(obj,arguments)
    //如果构造函数返回值是对象则返回这个对象,如果不是对象则返回新的实例对象
     return typeof res ==='object'?res:obj
    }

    还可以用Object.create(),他的属性值是放在原型下面的。

    function myNew(fn,...args){
      var obj = Object.create(fn.prototype)
      var res = fn.apply(obj,...args)
      return typeof res ==='object'?res:obj //new里面可能会有返回值,返回值是对象
    }

    例子:

    function Person(name){
        this.name = name
    }
    var old = new Person('用new的name')
    var new1 = myNew(Person,'没用new的name')
    
    console.log(old,new) //{name: "用new的name"} , {name: "没用new的name"}

    三.Object.create(obj,propertiesObject)

    obj:创建对象的原型,表示要继承的对象 必选

    propertiesObject:也是一个对象,用于对新创建的对象进行初始化

    底层原理

    Object.create = function(o){
      var F = function(){}
      F.prototype = o
      return new F()    
    }

    继承的应用

    var A = funtion(){}
    A.prototype.say = function(){
    console.log('a')
    }
    //B的实例继承了A的属性
    var B = function(){}
    B.prototype = Object.create(A.prototype)
    var b = new B()
    b.say() //a
    //重点:相对于构造函数的继承(new Object),Object.create继承实现了将A、B的原型完美分隔,双方不会互相影响,这是Object.create亮点所在

    Object.create() VS new Object()

    1.创建对象的方式不同

    new Object()通过构造函数来创建对象,添加的属性是在自身实例下

    Object.create() es6创建对象的另一种方式,可以理解为继承一个对象,添加的属性是在原型下

    //new Object()方式创建
    var a = {res:'apple'}
    var b = new Object(a)
    console.log(b) //{res:'apple'}
    b.__proto__ //{}
    b.res //{res:'apple'}
    
    //Object.create()方式创建
    var a = {res:'apple'}
    var b = Object.create(a)
    console.log(b) //{}
    console.log(b.__proto__) //{res:'apple'}
    console.log(b.res) //{res:'apple'}
    //Object.create()方法创建对象时,属性是在原型下面的,也可以直接访问b.res此时这个值不是b自身的,是它通过原型链proto来访问到b的值

    四.js防抖和节流

    防抖和节流是js性能优化很重要的一点,它们主要针对在一些短时间内被频繁触发的事件,例如:监听输入框的输入事件来验证表单,监听页面的滚动事件来实现列表的加载,窗口的resize事件等等,这些事件都有触发频率高,间隔时间短的特点,如果这个事件的回调函数涉及到很多的计算以及DOM的重绘的话,就可能会导致卡顿,影响用户的体验,所以最好用防抖和节流函数处理一下

    防抖(debounce)

    原理:在事件触发一定毫秒之后再执行

    function debounce(func,wait){
     let timer
     return function(){
     let self = this;
     let args = arguments
    if(timer){
      clearTimeout(timer)
    }
    timer = setTimeout(function(){
      func.apply(self,arguments)
    },wait)
    }
    }
    
    //使用
    window.onscroll = debounce(function(){console.log('debounce')},1000)

    节流(Throttle)

    原理:保证回调函数在一个时间段内只执行一次,通过计算时间差,如果已经执行过了,清除定时器,重新开始计时,否则就执行回调函数(每隔一段时间触发一次,像水滴一样)

    function throttle(fn,wait){
        let preTime = Date.now()
        return function(){
        let curTime = Date.now()
        if(curTime - preTime >wait){
        fn.apply(this,arguments)
        preTime  = curTime 
        }
        }
    }
    //应用
    window.onscroll = throttle(function(){
    console.log('throttle')
    },1000)

     五. es6的generator

    主要用于异步编程,最大的特点是可以交出函数的执行权(即暂停执行)

    和普通函数的区别

    1:function关键字与函数名之间有一个星号

    2:就是Generator函数体内部使用yield语句,可以定义不同的内部状态(内部的状态,就是函数内部的值,它在不同时候 是不一样的)

    next方法可以接收参数,传入的参数,是把上一个yield语句的返回的值给覆盖了

    第一个 .next()方法其实是启动器,在它之前没有yield语句,所以给第一个.next()方法去传参是没意义的

    generator函数 支持for of循环

  • 相关阅读:
    mysql采坑笔记
    git常用操作
    vscode配置及插件
    atom之插件安装及相关
    xshell中操作服务器笔记
    js学习笔记之自调用函数、闭包、原型链
    dragover event 翻译
    拖放事件笔记
    关于clear:both;后有固定高度的原因及解决方法
    weex打包android apk采坑之旅(windows)
  • 原文地址:https://www.cnblogs.com/alhh/p/11891229.html
Copyright © 2020-2023  润新知