call
函数实例的call
方法,可以指定函数内部this
的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数。call方法传入的参数第一个为调用的对象,后面的参数是按顺序传入方法。
var obj = {};
var f = function () {
return this;
};
f() === window // true
f.call(obj) === obj // true
手写源码实现
- 将obj.fn指定为调用的fn,即this
- 执行obj.fn
删除obj.fn
function mycall(){
//传入的第一个参数永远是调用的参数,如果不存在则默认为window
var ctx=arguments[0]||window
ctx.fn=this
//将传入的参数设置为一个数组
var args = [];
for(let i=1;i<arguments;i++){
args.push(arguments[i])
}
//将参数传入ctx
var result = ctx.fn(...args)
//删除fn
delete ctx.fn
return result
}
apply
apply跟call作用一样,唯一的不同是传入的参数是两个,第一个是调用的对象,第二个是参数数组。所以更改一下传入的参数即可
function myapply(obj,args){
obj.fn=this
var arrs=args||[]
var result = obj.fn(arrs.join(","))
delete ctx.fn
return result
}
bind
一个函数通过调用bind()方法创建一个新的绑定函数,调用新绑定函数,会在指定的作用域中传入参数并执行。使用bind()方法函数创建一个新绑定函数,它包装了原函数对象,调用绑定函数通常会导致执行包装函数。
简单的说法,bind返回的函数调用的时候,其实就调用apply,不过注意的是,bind传入的参数跟call的方式是一样的,是一个个传入的function mybind(){
var self=this
var obj=arguments[0]
var args=[...arguments].slice(1)
return function(){
self.apply(obj, args)
}
}
new
new 通过构造方法生产新的对象。
实现new:创建一个空对象,作为将要返回的对象实例。
- 将这个空对象的原型,指向构造函数的prototype属性。
- 将这个空对象赋值给函数内部的this关键字。
开始执行构造函数内部的代码。
也就是说,构造函数内部,this指的是一个新生成的空对象,所有针对this的操作,都会发生在这个空对象上。构造函数之所以叫“构造函数”,就是说这个函数的目的,就是操作一个空对象(即this对象),将其“构造”为需要的样子。
new命令总是返回一个对象,要么是实例对象,要么是return语句指定的对象。如若return语句返回的是字符串,new命令就会忽略了该语句。
function myNew(fn,...args){
if(typeof fn!=='function){
return false
}
var newobj={}
// fn.prototype.constructor=fn
newobj._proto_=fn.prototype
let result=fn.call(newobj,...args)
return typeof result==='object'?result:newobj
}
promise
其实前面异步编程中已经实现了promise的源码,这边一起整理下,并且加上Promise.all跟Promise.race的源码实现。
Promise.all
promise.all传入的是一个数组,数组的所有项都是promise对象,如果所有的promise对象都resolved,那么Promise.all获得的一个数组,数组内对应的是各个promise对象的值,但是其中只要有一个promise对象被reject了,那么promise返回的就是reject的值。
Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。
- 先用new Promise包装成一个新的promise对象
- 定义一个方法跟数组,用来判断相应的执行操作,还有存入相应位置的值
- 循环执行传入的promise对象数组,并判断是否是promise对象
- 一旦获取到reject,就返回reject的值
```javascript
// 校验是否是 promise
function isPromise(x) {
if((typeof x ===’object’ && x !== null) || typeof x ===’function’) {
} return false }if(typeof x.then == 'function') {
return true
}
Promise.all = function(promises) { //因为Promise.all返回的是一个新的promise对象,所以要套一层new Promise return new Promise((resolve, reject) => { let arr = [] let idx = 0 // 执行个数 //将每个执行项加入到dealProcess中,每次执行这个方法就将idx加1 let dealProcess = (val, index) => { arr[index] = val if(++idx == promises.length) { resolve(arr) } } promises.forEach((item, i) => { if(isPromise(item)) { item.then(y => { dealProcess(y, i) },reject) }else { dealProcess(item, i) } }); }) }
<a name="0NKMW"></a>
### Promise.race
这个很简单,同promise一样返回一个新的promise对象,然后谁先执行就直接resolve。直接进行循环处理就行了,只要谁resolve了就直接抛出来
```javascript
Promise.race(promises){
return newPromis((resolve,reject)=>{
promises.forEach(item=>{
item.then(res=>{resolve(res)}).catch(err=>{reject(err)})
})
}
)
}
instanceof
boolean
result = obj ``instanceof
Object
instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上。
只要一直用obj=obj.proto顺着原型链一直往上找就好了。
function instance_of(L,R){
const baseType = ['string', 'number','boolean','undefined','symbol']
if(baseType.includes(typeof(L))) { return false }
let RP = R.prototype; //取 R 的显示原型
L = L.__proto__; //取 L 的隐式原型
while(true){ // 无线循环的写法(也可以使 for(;;) )
if(L === null){ //找到最顶层
return false;
}
if(L === RP){ //严格相等
return true;
}
L = L.__proto__; //没找到继续向上一层原型链查找
}
}
Object.create
**Object.create()**
方法创建一个新对象,使用现有的对象来提供新创建的对象的proto。
function create(obj){
let fn=obj._proto_.constructor
let newobj=new fn()
return newobj
}
防抖与节流
防抖函数:短时间内大量操作的最后一次,只有满足最后一次的那段时间没有被触发,就会执行。可以通过设置一个timer,如果timer存在并且又被触发,则重置timer,这样子除非最后一次才会触发
防抖常用于搜索框/滚动条/按钮点击 的监听事件处理,如果不做防抖,每输入一个字/滚动屏幕,都会触发事件处理,造成性能浪费
function debounce(fn,second){
let timer
return function(args){
if(timer){
clearTimeout(timer)
}
timer=setTimeout({
timer=null
fn.call(this,...args)
},second)
}
}
节流函数,限制在一定时间内只触发一次。而节流在定时器到时间后再清空定时器。实现原理:在点击后执行时间,然后这段时间内的所有点击都无效。可以利用锁机制来判断是否这段时间已经过了。突然发现平时用了好多节流的场景,其实就是利用锁机制。
应用场景 => DOM元素拖拽 射击游戏 计算鼠标距离 scroll滚动事件
function throttel(fn,delay){
let lock='unlock'
if(lock==='lock'){
return false
}
lock='lock'
return function(args){
lock='lock'
let timer=setTimeout({
fn.call(this,...args)
lock='unlock'
},delay)
}
}
深拷贝deepClone
说到底,想要实现深拷贝,就是浅拷贝加递归,也就是如果对象的属性值还是一个对象的话,再进行一次拷贝
//判断是否是对象
function isObject(obj) {
return typeof obj === 'object' && obj != null;
}
function deepCopy(source){
// 判断如果参数不是一个对象,返回改参数
if(!isObject(source)) return source;
// 判断参数是对象还是数组来初始化返回值
let res = Array.isArray(source)?[]:{};
// 循环参数对象的key
for(let key in source){
// 如果该key属于参数对象本身
if(Object.prototype.hasOwnProperty.call(source,key)){
// 如果该key的value值是对象,递归调用深拷贝方法进行拷贝
if(isObject(source[key])){
res[key] = deepCopy(source[key]);
}else{
// 如果该key的value值不是对象,则把参数对象key的value值赋给返回值的key
res[key] = source[key];
}
}
}
// 返回返回值
return res;
}
reduce
这是ES6的一个语法,简单来说就是循环返回前面处理好的值。
Array.prototype.reduce = function (callback,prev) {
//遍历this 数组
for (let i = 0; i < this.length; i++) {
//判断有没有设置初始值
if (typeof prev === "undefined") {
//没有初始值,则调用callback,转入当前值,下一个值,当前index为下一个,当前数组
prev = callback(this[i], this[i + 1], i + 1, this);
} else {
//有初始值,则调用callback,传入prev为第一个值,第二个为当前的i,当前index为i,当前数组
prev = callback(prev, this[i], i, this);
}
}
return prev;
};
arr.reduce((pre,cur,index,arr)=>{})