call(),apply(),bind()
call
1调用函数
2改变函数内的this指向
3实现继承: 构造函数+ 原型对象--组合继承
var obj = { uname: 'lili' } function fn(a, b) { console.log(a + b); console.log(this); } fn.call(); // 1调用函数 fn.call(obj, 1, 2); // 改变this指向 function Father(uname,age) { this.uname = uname; this.age = age; } Father.prototype.money = function() { console.log(this); } function Son(uname, age, sex) { Father.call(this, uname, age); // 3 实现继承 this.sex = sex;; } // Father的实例对象中有__proto_ ,指向Father的原型对象prototype,Father的prototype中就有Father的方法 Son.prototype = new Father(); // 如果利用实例对象的形式修改了原型对象,要把constructor 指回构造函数 Son.prototype.constructor = Son; Son.prototype.exam = function() { console.log('孩子的方法'); }
apply - 第二个参数要传数组
1调用函数
2改变函数内的this指向
3 利用apply借助于数学内置对象求最大值
var obj = { uname: 'lili' } function fn(arr) { console.log(arr); console.log(this); } fn.apply(); // 1调用函数 fn.apply(obj, [1, 2]); // 改变this指向 var arr = [3,66,8,77,9]; // var max = Math.max.apply(null, arr); var max = Math.max.apply(Math, arr); var min = Math.min.apply(Math, arr);
补充点相关点
Math.max()函数返回一组数中的最大值。
console.log(Math.max(1, 3, 2)); // 3
es6写法
剩余运算符...:把多个独立的合并到一个数组
拓展运算符...:把一个数组分割,传给函数
const arr = [1,3,2];
console.log(Math.max(...arr));
bind
1不会调用原来的函数,只改变函数内部this指向
2返回原函数改变this指向后的新函数
3如果有的函数不需要立即调用,但又需要改变this指向 --定时器里的函数
var obj = { uname: 'lili' } function fn(a, b) { console.log(a + b); console.log(this); } var f = fn.bind(obj, 1, 2); // 不调用函数,只改变this指向 f(); // 再去调用新函数 // 如果有的函数不需要立即调用,但又需要改变this指向 var btn = document.querySelector('button'); btn.onclick = function() { this.disabled = true; setTimeout(function() { // 定时器里的函数this指向window this.disabled = false; }.bind(this), 3000) // 这个this是btn }
js call,apply方法实现原理
Function.prototype.myCall = function () { let [targetObj, ...arg] = [...arguments] /* 重点 用传递过来的对象 添加一个属性赋值为this */ targetObj.fn = this; // 对象的隐式调用,就是在A.call(B)的时候,在call函数内部的this是指向A的 let result = targetObj.fn(...arg) delete targetObj.fn // 在targetObj中是没有这个属性的所有要删除这个属性 return result; } var obj = { name: 'ooo' } function sayHi(e) { console.log(this); }
sayHi.myCall(obj, 1, 2, 3);
apply与call的实现原理相同,就是传参的方式略微不同。传的是一个数组。
Function.prototype.myApply = function () { let [targetObj, ...arr] = [...arguments] targetObj.fn = this; let result = targetObj.fn(...arr); delete targetObj.fn; return result; } var obj = { name: 'ooo' } function sayHi(e) { console.log(this); }
sayHi.myApply(obj, [1, 2, 3]);
js bind方法实现原理
Function.prototype.bind_ = function (obj) { //第0位是this,所以得从第一位开始裁剪 var args = Array.prototype.slice.call(arguments, 1); var fn = this; return function () { //二次调用我们也抓取arguments对象 var params = Array.prototype.slice.call(arguments); //注意concat的顺序 fn.apply(obj, args.concat(params)); }; }; var obj = { z: 1 }; function fn(x, y) { console.log(x + y + this.z); }; var bound = fn.bind_(obj, 1); bound(2); //4
Array.prototype.slice.call()
JavaScript中的Array.prototype.slice.call(arguments)能将有length属性的对象转换为数组
特别注意: 这个对象一定要有length属性
let args = [].slice.call(arguments);意思是将 arguments 数组化。
arguments 是一个类数组的结构,它并不是一个真的数组,所以没法使用数组的方法。