call和apply的用途
Function.prototype.call 和 Function.prototype.apply 都是非常常用的方法。它们的作用一模一样,区别仅在于传入参数形式的不同
方法 apply call
第一个参数 函数体内 this 对象的指向 函数体内 this 对象的指向
第二个参数 带下标的集合 从第二个参数开始,每个参数被依次传入函数
apply
var func = function( a, b, c ){
alert ( [ a, b, c ] ); // 输出 [ 1, 2, 3 ]
};
func.apply( null, [ 1, 2, 3 ] );
call
var func = function( a, b, c ){
alert ( [ a, b, c ] ); // 输出 [ 1, 2, 3 ]
};
func.call( null, 1, 2, 3 );
//实现一个 将类数组 或对象 转化对 真正数组
function array(a, n){
return Array.prototype.slice.call(a, n || 0)
}
//应用: 这个函数的实参传至左侧
function partialLeft (f /,.../){
var args = arguments;
return function(){
var a =array(args,1);
a = a.concat(array(arguments));
return f.apply(this,a)
}
}
call和apply的用途
改变 this 指向
借用其他对象的方法
改变 this 指向
var obj1 = {
name: 'sven'
};
var obj2 = {
name: 'anne'
};
window.name = 'window';
var getName = function(){
alert ( this.name );
};
getName(); // 输出:window
getName.call( obj1 ); // 输出: sven
getName.call( obj2 ); // 输出: anne
借用其他对象的方法
(function(){
Array.prototype.push.call( arguments, 3 );
console.log ( arguments ); // 输出[1,2,3]
})( 1, 2 );
Function.prototype.myCall = function(context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
context = context || window;console.log('context==>',context)
context.fn = this;console.log("context.fn==>",context.fn,"this==>",this)
const args = [...arguments].slice(1);console.log('arg==>s',args);
const result = context.fn(...args)
delete context.fn; console.log('result==>',result)
return result
}
//准备两个方法来玩一下
function add(a,b){
return a+b;
}
function sub(a,b){
return a-b;
}
//控制台打印
add.myCall(sub,32,122) //sub 调用add方法
context==> ƒ sub(a,b){
return a-b;
}
context.fn==> ƒ add(a,b){
return a+b;
} this==> ƒ add(a,b){
return a+b;
}
arg==>s (2) [32, 122]
result==> 154
154
//以下是对实现的分析:
//首先 context 为可选参数,如果不传的话默认上下文为 window
//接下来给 context 创建一个 fn 属性,并将值设置为需要调用的函数
//因为 call 可以传入多个参数作为调用函数的参数,所以需要将参数剥离出来
//然后调用函数并将对象上的函数删除
//apply() 实现
Function.prototype.myApply = function(context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
context = context || window
context.fn = this
let result
// 处理参数和 call 有区别
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
//apply 用于实现继承
function Animal(name){
this.name = name;
this.showName = function(){
alert(this.name);
}
}
function Cat(name){
Animal.apply(this,[name]);
}
var cat = new Cat("咕咕");
cat.showName();
/call的用法/
Animal.call(this,name);
bind() 方法
bind()在EC5中新增的方法。这个方法的主要作用就是将函数绑定至某个对象。
function f(y) { return this.x + y} //这个是待绑定的方法
var o = { x : 1};//将要绑定的对象
var g = f.bind(o) ; // 通过调用g(x)来调用 o.f(x)
g(2 ) // ==> 3
//其他例子
this.num = 9;
var mymodule = {
num: 81,
getNum: function() { return this.num; }
};
module.getNum(); // 81
var getNum = module.getNum;
getNum(); // 9, 因为在这个例子中,"this"指向全局对象
// 创建一个'this'绑定到module的函数
var boundGetNum = getNum.bind(module);
题外疑问:最近读到var a = Function.prototype.bind() ; 这个函数a没有 prototype的 即a.prototype 没有
所以我用下面的方法 var c = Function.prototype.mybind1() 却带有 c.prototype
所以 Function.prototype.myBind1() === Function.prototype.bind() //false
// 通过如下代码实现这种绑定
Function.prototype.myBind1 = function (context) { console.log('this==>',this)
if (typeof this !== 'function') {
throw new TypeError('Error')
}
const _this = this
const args = [...arguments].slice(1);console.log("args==>",args)
// 返回一个函数
return function F() {
// 因为返回了一个函数,我们可以 new F(),所以需要判断
if (this instanceof F) { console.log('F==>',F)
return new _this(...args, ...arguments)
} console.log('result==>',_this.apply(context, args.concat(...arguments)))
return _this.apply(context, args.concat(...arguments))
}
}
new
//在调用 new 的过程中会发生以上四件事情:
//新生成了一个对象
//链接到原型
//绑定 this
//返回新对象
//根据以上几个过程,我们也可以试着来自己实现一个 new
function Person(name,age){
this.name = name;
this.age = age;
}
function create() {
// 创建一个空的对象
let obj = new Object()
// 获得构造函数
let Con = [].shift.call(arguments)
// 链接到原型
obj.proto = Con.prototype
// 绑定 this,执行构造函数
let result = Con.apply(obj, arguments)
// 确保 new 出来的是个对象
return typeof result === 'object' ? result : obj
}
var p2 = create(Person,"涛涛",333) //等同于 var p2 =new Person("涛涛",333)
p2//==> Person {name: "涛涛", age: 333}
以下是对实现的分析:
创建一个空对象
获取构造函数
设置空对象的原型
绑定 this 并执行构造函数
确保返回值为对象
对于对象来说,其实都是通过 new 产生的,无论是 function Foo() 还是 let a = { b : 1 } 。
对于创建一个对象来说,更推荐使用字面量的方式创建对象(无论性能上还是可读性)。因为你使用 new Object() 的方式创建对象需要通过作用域链一层层找到 Object,但是你使用字面量的方式就没这个问题。
function Foo() {}
// function 就是个语法糖
// 内部等同于 new Function()
let a = { b: 1 }
// 这个字面量内部也是使用了 new Object()
instanceof
instanceof 可以正确的判断对象的类型,因为内部机制是通过判断对象的原型链中是不是能找到类型的 prototype。
我们也可以试着实现一下 instanceof
function myInstanceof(left, right) {
let prototype = right.prototype;console.log('prototype--',prototype)
left = left.proto ;console.log('left--',left)
while (true) {
if (left === null || left === undefined)
return false
if (prototype === left)
return true
left = left.proto
}
}
var a = {}
a instanceof Object //true
myInstanceof(a,Object) // true 可以自行尝试下
以下是对实现的分析:
首先获取类型的原型
然后获得对象的原型
然后一直循环判断对象的原型是否等于类型的原型,直到对象原型为 null,因为原型链最终为 null
参考 : https://juejin.im/post/5c0d15a3f265da612e2876a2
参考 :bind()_前进的探索者的博客-CSDN博客