在学习this的时候,看到了几个方法call,apply和bind还有一个new 运算符,就学习了一下它们的概念和原理
function.call(thisArg,arg1,arg2…)
使用一个指定的this值 和单独给出的一个或多个参数来调用函数,返回值是调用有指定this值和参数的函数的结果
注意:
1.非严格模式下如果thisArg是null 或者是 undefined 则自动替换为全局对象
2.thisArg可以是基本数据类型,call会自动转换为原始数据类型
// call的演示
var animals = [
{
species: 'Lion', name: 'King' },
{
species: 'Whale', name: 'Fail' }
];
for (var i = 0; i < animals.length;i++){
(function (i) {
this.point = function () {
console.log(`#${
i}:${
this.species}+${
this.name}`)
}
}).call(animals[i],i)
}
//给数组的每一项对象添加一个方法
原理
Function.prototype.myCall = function (context) {
/** 如果第一个参数传入的是 null 或者是 undefined, 那么指向this指向 window/global */
/** 如果第一个参数传入的不是null或者是undefined, 那么必须是一个对象 */
if (!context) {
//检测一下全局对象是window还是global
context = typeof window === 'undefined' ? global : window;
}else{
context = Object(context)
}
context.fn = this; //this指向的是当前的函数(Function的实例)
let rest = [...arguments].slice(1);//获取除了this指向对象以外的参数, 空数组slice后返回的仍然是空数组
let result = context.fn(...rest); //隐式绑定,当前函数的this指向了context.
delete context.fn; //消除副作用
return result; //返回这个函数的返回值
}
测试
var foo = {
name: 'Selina'
}
var name = 'Chirs';
function bar(job, age) {
console.log(this.name);
console.log(job, age);
}
bar.myCall(foo, 'programmer', 20); //Selina, programmer , 20
bar.myCall(null, 'programmer', 20); //Chirs, programmer , 20
function.apply(thisArg,[argsArray])
使用一个指定的this值 和一个数组对象(或者是类数组对象)来调用函数,非严格模式下如果thisArg是null 或者是 undefined 则自动替换为全局对象,返回值是调用有指定this值和参数的函数的结果
// 使用apply将一个数组的所有项 添加到另一个数组中
let charArr = ['a','b','c']
let numArr = [1,2,3]
numArr.push.apply(numArr,charArr)
console.log(numArr) //[1 , 2 , 3 , 'a' , 'b' ,'c']
原理 跟call相似
Function.prototype.myApply = function(context,rest){
//接收两个参数 一个是指定的调用对象 一个是数组参数
if(!context){
//跟call一样,如果第一个参数是null或者是undefined的情况,检测一下全局对象
context = typeof window === undefined ? global : window
}else{
context = Object(context)
}
context.fn= this
let result;
if(rest === undefined || rest === null){
//第二个参数是undefined或者是null的话不能被...
result = context.fn(rest)
} else if (rest instanceof Object ){
result = context.fn(...rest)
}
delete context.fn //消除副作用
return result
}
测试
//测试代码
var foo = {
name: 'Selina'
}
var name = 'Chirs';
function bar(job, age) {
console.log(this.name);
console.log(job, age);
}
bar.myApply(foo, ['programmer', 20]); //Selina programmer 20
bar.myApply(null, 'teacher', 25);
//浏览器环境: Chirs teacher 25; node 环境: undefined teacher 25
function.bind(thisArg,arg1,arg2…)
bind 创建一个新的函数 而新函数的this被指定为bind的第一个参数,而其余的参数作为新函数的参数,返回一个源函数的拷贝函数 并拥有指定的this值和参数
注意:
1.多次bind无效,只会绑定在第一个上面
2.bind返回的是源函数拷贝,所以调用bind的必须是一个函数
3.使用new调用bind返回的函数的时候,this指向的是源函数构造函数的实例
Function.prototype.myBind = function (thisArg,...rest) {
//如果调用者不是函数 报错
if (typeof this !== 'function') throw new TypeError("invalid invoked!");
let self = this
//返回源函数的拷贝函数
return function F(...args) {
console.log(this)
if(this instanceof F){
// 说明新返回的对象被 new操作符调用 this指向那个构造函数对象实例
return new self(...rest,...args)
// 就返回源函数的构造函数的实例
}
// 否则就调用那个函数
return self.apply(thisArg,[...rest,...args])
}
}
测试
var name = 'window'
function fn(name) {
// console.log(this)
console.log(this.name)
console.log(name)
}
let obj = {
name:'obj'
}
let f= fn.myBind(obj)
f('小明') //obj 小明
let nF = new f('小白') // undefined 小白
new constructor[([arguments])]
constructor 一个指定实例的类型的类或者是函数
arguments 参数列表
new 运算符创建一个用户定义的对象类型的实例或者是具有构造函数内置对象的实例
new 关键字会进行以下操作
- 创建一个新的对象
- 将这个对象的__proto__指向该构造函数的原型
- 将这个对象作为函数的this的上下文
- 如果这个构造函数没返回对象 则返回这个新对象
function Person(name,age) {
this.name = name
this.age = age
}
let p = new Person('小明',23)
console.log(p) //Person {
name: '小明', age: 23 }
原理
function myNew(fun, ...arg) {
// 创建一个新的对象
const result = {
};
// 该对象的__proto__属性指向该构造函数的原型
result.__proto__ = fun.prototype
fun.prototype.constructor == fun
// 将执行上下文(this)绑定到新创建的对象中
const res = fun.apply(result, arg);
// 如果函数的返回值是个对象
if(res&&res instanceof Object){
return res
}
//否则就返回这个新创建的对象
return result;
}
测试
function Person(name,age) {
this.name = name
this.age = age
}
let p = myNew(Person,'小明',22)
console.log(p) //Person {
name: '小明', age: 22 }
function Person(name,age) {
this.name = name
this.age = age
return {
}
}
let p = myNew(Person,'小明',22)
console.log(p) //{
}