一、call,apply,bind
call,apply,bind都是用于改变this指向的
区别:
传参不同
call用逗号分隔的形式传参
函数名.call(目标对象,参数1,参数2,…参数n) 例如:getName.call(obj,‘王五’,25,‘北京’)
apply参数用数组的形式传递
函数名.apply(目标对象,[参数1,参数2,…参数n]) 例如:getName.apply(obj,[‘王五11’,25,‘上海’])
bind用逗号形式传参
getName.bind(obj,‘王五11’,25,‘上海’)() 或 getName.bind(obj)(‘王五11’,25,‘上海’) 函数是否被执行
cal和apply是直接调用函数
bind是返回函数本身,如果要执行,必须再后面再加()调用
getName.bind(obj)()
call,apply实现原理
call的实现原理(不用call,自己手动模拟实现一个call的功能)
call是基于函数实现的 给作用的目标对象添加一个临时函数,处理赋值操作 接收参数处理 最后再删除这个临时函数 实例化对象=new 构造函数() //其中构造函数也称为类,一个类可以生成多个实例化对象 var f1=new Function() // 其中的构造函数中this===f1 永远相等 var p1=new Person() //其中的构造函数中this===p1 永远相等 //call模拟实现原理代码: Function.prototype.call2 = function (context) { //目标对象 context = context || window; //this===实例化的函数,函数本质上也是对象 //给context添加一个临时函数 context.fn = this; //接收参数处理 arguments console.log('arguments:',arguments) var args = []; for (var i = 1; i < arguments.length; i++) { // ["arguments[0]", "arguments[1]", "arguments[2]"] args.push('arguments['+i+']') // args.push(arguments[i]) } //传参执行context.fn()函数 eval('context.fn(' + args + ')') //删除临时函数 delete context.fn }
apply实现原理
Function.prototype.apply2 = function (context,arr) { //目标对象 context = context || window; //this===实例化的函数,函数本质上也是对象 //给context添加一个临时函数 context.fn = this; if (!arr) { context.fn() } else { //接收参数处理 arguments var args = []; for (var i = 0; i < arr.length; i++) { // ["arguments[0]", "arguments[1]", "arguments[2]"] args.push('arr['+i+']') // args.push(arguments[i]) } //传参执行context.fn()函数 eval('context.fn(' + args + ')') } //删除临时函数 delete context.fn }
bind实现原理
var obj = { init: 1, add: function(a, b) { return a + b + this.init; } } obj.add(1, 2); // 4 var plus = obj.add; plus(3, 4); // NaN,因为 this.init 不存在,这里的 this 指向 window/global plus.call(obj, 3, 4) // 8 plus.apply(obj, [3, 4]); // 8, apply 和 call 的区别就是第二个参数为数组 plus.bind(obj, 3, 4); // 返回一个函数,这里就是 bind 和 call/apply 的区别之一,bind 的时候不会立即执行
总结
就是简单的使用 bind 来改变 this 指向,和 call/apply 的区别,有一个延迟执行的作用
二、new的实现原理
new的特点
new 一个构造函数,会自动reutrn一个实例化对象
new完的实例化对象____proto___自动指向构造函数的prototype
new构造函数传参自动赋值给当前实例化对象
三、防抖和节流
防抖概念
在固定的时间内没有触发事件,会在固定时间结束后触发,如果固定时间内触发事件了,会在延长固定时间再触发
防抖主要利用定时器实现
//用定时器实现防抖 function debounce(func,wait) { var timer=null; return function() { //保存当前调用的dom对象 var _this=this; //保存事件对象 var args=arguments; clearTimeout(timer) timer=setTimeout(function() { func.apply(_this,args) },wait) } }
节流概念:
无论在固定时间内是否有事件触发,都会按照固定时间规律触发
具体实现有两种方法
第一种:时间戳
//时间戳版本实现节流 function throttle(func,wait) { //定义初始时间 var oldTime=0; return function() { var _this=this; var args=arguments; //当前时间戳 var newTime=+new Date(); //判断用当前时间减去旧的时间,如果大于wait指定的时间就会触发 if(newTime-oldTime>wait) { //执行触发的函数 func.apply(_this,args) //将旧时间更新 oldTime=newTime; } }
第二种:定时器
//时间戳版本实现节流 function throttle(func,wait) { var timer=null; return function() { var _this=this; var args=arguments; if(!timer) { timer=setTimeout(function() { timer=null; func.apply(_this,args) },wait) } } }
当然如果以上都不精通可以用目前市面主流工具函数库:lodash已经提供了全面的工具方法
详情请参考lodash网站:https://www.lodashjs.com/.