在实际开发过程当中,对于函数封装时,不肯定外部是谁调用的,调用函数内部方法时,有多是window
调用这时就会报错,常使用call
,apply
,bind
来绑定this
指向。数组
call()
方法调用一个函数, 其具备一个指定的this值和分别地提供的参数。浏览器
该方法和apply()
相似,区别在于,call()
能够接收若干参数,而apply()
接收的是一个包含多个参数的数组。bash
语法:fun.call(thisArg, arg1, arg2, ...)
app
经过父类的构造函数call
方法实现继承函数
function Product(name, price) {
this.name = name;
this.price = price;
}
function Food(name, price) {
Product.call(this, name, price);
this.category = 'food';
}
var cheese = new Food('feta', 5);
console.log(cheese)
// Food { name: 'feta', price: 5, category: 'food' }
复制代码
实例都会拥有在Product构造函数中添加的name属性和price属性,但category属性是在各自的构造函数中定义的。ui
var animals = [
{ species: 'Lion', name: 'King' },
{ species: 'Whale', name: 'Fail' }
];
for (var i = 0; i < animals.length; i++) {
(function(i) {
console.log('#' + i + ' ' + this.species + ': ' + this.name) }
).call(animals[i], i);
}
复制代码
for循环体内,咱们建立了一个匿名函数,而后经过调用该函数的call方法,将每一个数组元素做为指定的this值执行了那个匿名函数。this
function greet() {
var reply = [this.animal, 'typically sleep between', this.sleepDuration].join(' ');
console.log(reply);
}
var obj = {
animal: 'cats', sleepDuration: '12 and 16 hours'
};
greet.call(obj);
// cats typically sleep between 12 and 16 hours
复制代码
Function.prototype.myCall = function(context) {
context = context ? Object(context) : window
context.fn = this
let args = [...arguments].slice(1)
let r = context.fn(args)
delete context.fn
return r
}
复制代码
apply()
调用一个指定this
值的函数, 接收做为一个数组或者类数组对象提供的参数spa
语法: func.apply(thisArg, [argsArray])
prototype
var array = ['a', 'b'];
var elements = [0, 1, 2];
array.push.apply(array, elements);
console.log(array); // ["a", "b", 0, 1, 2]
复制代码
var numbers = [5, 6, 2, 3, 7];
var max = Math.max.apply(null, numbers)
var min = Math.min.apply(null, numbers);
复制代码
若是参数组很是大,将参数数组切块后,循环传入目标方法:code
function minOfArray(arr) {
var min = Infinity;
var QUANTUM = 32768;
for (var i = 0, len = arr.length; i < len; i += QUANTUM) {
var submin = Math.min.apply(null, arr.slice(i, Math.min(i + QUANTUM, len)));
min = Math.min(submin, min);
}
return min;
}
var min = minOfArray([5, 6, 2, 3, 7]);
console.log(min) // 2
复制代码
Function.prototype.myApply = function(context) {
context = context ? Object(context) : window
context.fn = this
let args = [...arguments][1]
if (!args) {
return context.fn()
}
let r = context.fn(args)
delete context.fn;
return r
}
复制代码
bind()
方法建立一个新函数, 在调用时设置this关键字为提供的值。
并在调用新函数时,将给定参数列表做为原函数的参数序列的前若干项。 语法: function.bind(thisArg, [arg1[, arg2[, ...]]])
his.x = 9; // 在浏览器中,this指向全局的 "window" 对象
var module = {
x: 81,
getX: function() { return this.x; }
};
module.getX(); // 81
var retrieveX = module.getX;
retrieveX(); // 返回9 - 由于函数是在全局做用域中调用的
var boundGetX = retrieveX.bind(module); // 建立一个新函数,把 'this' 绑定到 module 对象
boundGetX(); // 81
复制代码
function list() {
return Array.prototype.slice.call(arguments);
}
function addArguments(arg1, arg2) {
return arg1 + arg2
}
var list1 = list(1, 2, 3); // [1, 2, 3]
var result1 = addArguments(1, 2); // 3
// 建立一个函数,它拥有预设参数列表。
var leadingThirtysevenList = list.bind(null, 37);
// 建立一个函数,它拥有预设的第一个参数
var addThirtySeven = addArguments.bind(null, 37);
var list2 = leadingThirtysevenList();
// [37]
var list3 = leadingThirtysevenList(1, 2, 3);
// [37, 1, 2, 3]
var result2 = addThirtySeven(5);
// 37 + 5 = 42
var result3 = addThirtySeven(5, 10);
// 37 + 5 = 42 ,第二个参数被忽略
复制代码
当 fn1.myCall(fn2)
时,绑定当前this
须要context.fn = this
等价于context.fn = fn1
调用的时候 context.fn()
等价于 fn2.fn()
此时this
是fn2
并执行fn1
。
当fn1.myCall.myCall(fn2)
是此时都是执行myCall
函数, this
为window
, 并执行fn2
函数。
let obj = {
name: 'joker'
}
function fn() {
console.log(this.name)
}
Function.prototype.bind = function(context) {
}
let bindFn = fn.bind(obj)
bindFn()
// joker
复制代码
从上面例子能够看出
this
执行为传入的对象bind
方法Function.prototype.bind = function(context) {
let _me = this
return function() {
return _me.apply(context)
}
}
复制代码
bind 还能够屡次传参 用法:
let obj = {
name: 'joker'
}
function fn(name, age) {
console.log(this.name + '今年' + name + age + '岁了')
}
let bindFn = fn.bind(obj, '大概')
bindFn(10)
// joker今年大概10岁了
复制代码
绑定this
的时候传递了一个值, 执行bindFn
又传了一个参数,所以以前的函数须要改造
Function.prototype.bind = function(context) {
let _me = this
let bindArgs = [].slice.call(arguments, 1) // 获取bind方法传入的参数
return function() {
let fnArgs = [].slice.call(arguments) // 获取函数执行传入的参数
return _me.apply(context, bindArgs.concat(fnArgs))
}
}
复制代码
若是当前绑定的函数被new
了,当定函数中的this
是当前函数的实例,用法
let obj = {
name: 'joker'
}
function fn(name, age) {
console.log(this) // this是fn
}
let bindFn = fn.bind(obj)
let instance = new bindFn()
复制代码
那么这个方法还须要改造一下, 若是当前函数执行中的this
是fBound
的实例,说明是new
执行的,那么当前 this
就是函数的实例,不然是context
Function.prototype.bind = function(context) {
let _me = this
let bindArgs = [].slice.call(arguments, 1)
function Fn() {}
let fBound = function() {
let fnArgs = [].slice.call(arguments)
return _me.apply(this instanceof fBound ? this : context, bindArgs.concat(fnArgs))
}
Fn.prototype = this.prototype
fBound.prototype = new Fn();
return fBound
}
复制代码
想了解new的原理先要了解js的原型机制,先来看张图
let f1 = new Foo()
复制代码
f1
是构造函数 Foo
的实例, __proto__
指向构造函数的原型Foo.prototype
Foo.prototype.constructor
指向构造函数Foo
, Foo
的prototype
指向它的原型Foo
的原型的__proto__
最终指向Object
function Animal(type) {
this.type = type;
}
Animal.prototype.say = function() {
console.log('say')
}
function mockNew() {
let Constructor = [].shift.call(arguments); // 取出构造函数
let obj = {} // new 执行会建立一个新对象
obj.__proto__ = Constructor.prototype
Constructor.apply(obj, arguments)
return obj
}
let animal = mockNew(Animal, 'dog')
console.log(animal.type) // dog
animal.say() // say
复制代码