javaScript——this指针

437 阅读5分钟

什么是this

this指针指向一个对象, 具体指向哪个对象是在函数执行时才确定的。

不同情况下this的指向

1.(默认绑定)函数直接调用,在非严格模式下,this指向window,严格模式下,this指向undefined。

//放在普通函数中
function fn(){
    console.log(this);
    //window
}
fn();

//放在严格模式的函数中
function fn1(){
    "use strict"
    console.log(this);
    //undefined
}
fn1();

2.(隐式绑定)对象函数调用,this指向调用函数的对象

function out(){
  console.log(this.b);
}
let foo = {
  a:20,
  b:10,
  print:function(){
    console.log(this.a);
  },
  print2:out
}
foo.print();//20
foo.print2();//10

但是这种绑定也会出现隐式丢失的问题

var foo = {
  gla:20,
  print:function(){
    console.log(this.gla);
  },
}
foo.print();//20 这是指向的对象是foo
var gPrint = foo.print; //gPrint是函数别名
var gla = 30;//全局变量(所属是window)
gPrint();//30 这时候指向的对象是window

3.(new绑定) 构造函数调用,this指向新创建的对象

//ES5
function foo(a) {
    this.a = a;
    console.log(this);
}

var bar = new foo(2); // foo{a:2}

//ES6
class Person{
    constructor(name){
        this.name = name;
        console.log(this);
    }
    print(){
      console.log(this.name);
    }
}
let p=new Person("小红");//Person {name:"小红"}
let p1=new Person("小名");//Person {name:"小明"}
p.print();//小红
p1.print();//小明
let p2 = p.print;
p2();//Uncaught TypeError: Cannot read property 'name' of undefined

4.在箭头函数中没有单独的this, this在箭头创建的时候绑定(继承),与声明所在的上下文相同(找外层函数的this即可)。

let people = {
  name:function(){
    console.log(this); // 这里的this可能存在隐式丢失的情况,所以不一定指向people
    let first = ()=>{
      console.log(this);//这里的this一定与外层函数的this一致
    }
    first();
  },
  age:()=>{
    console.log(this);// 这里外层无函数,或者可以直接认为最外层函数就是window
  }
}
people.name();// {name: ƒ, age: ƒ}
people.age();// window
let fun = people.name;
fun(); // window

如何改变this的指向(call apply bind )

1.call和apply

call(要指向的对象,参数1,参数2,参数3)

apply(要指向的对象,[参数1,参数2,参数3]),效果和call一样,只是把参数换成数组形式

function people(age,name){
    console.log(this);
    console.log('cato:'+this.cato+' age:'+age+',name:'+name);
}

var cato = 'none';
people(11,'Tom');//单独调用的时候,this默认的是window  输出:window cato:none age:11,name:Tom

var man = {
  cato:'man'
}
people.call(man,20,'Bob');// 这个时候把people绑定在man上,输出:{cato: "man"} cato:man age:20,name:Bob
people.apply(man,[30,'isak']);//输出:{cato: "man"} cato:man age:30,name:isak

因为apply可以把数组元素迭代为函数参数,所以在很多地方有妙用。

Math.max()用在数组上

let answer = Math.max.apply(null, [2, 4, 3])
console.log(answer) // 4

// 注意下面三个等价
Math.max.apply(null, [2, 4, 3])
Math.max.call(null, 2, 4, 3)
Math.max(2, 4, 3)

转自:https://juejin.cn/post/6922800009620815885

合并两个数组

// 将第二个数组融合进第一个数组
// 相当于 arr1.push('celery', 'beetroot');
let arr1 = ['parsnip', 'potato']
let arr2 = ['celery', 'beetroot']

arr1.push.apply(arr1, arr2)
// 注意!!!this的意思是要指定调用了push这个方法
// 所以当 this = arr1 后
// 就成了 arr1 调用了 push方法
// 上述表达式等价于 arr1.push('celery', 'beetroot') 

console.log(arr1)
// ['parsnip', 'potato', 'celery', 'beetroot']

//以下三个语句完全等价
Array.prototype.push.apply(arr1, arr2)
arr1.push.apply(arr1, arr2)
arr2.push.apply(arr1, arr2)

转自:https://juejin.cn/post/6922800009620815885

2.bind call、apply是立即执行的,而bind是返回一个新函数,调用新函数的时候再执行。

function people() {
  console.log(this)
}
let Tom = {
	name:'Tom',
    age:14
}
let foo = people.bind(Tom);
foo();

测试题

// 情况1
function foo() {
  console.log(this.a) //1
}
var a = 1
foo()

// 情况2
function fn(){
  console.log(this);
}
var obj={fn:fn};
obj.fn(); //this->obj

// 情况3
function CreateJsPerson(name,age){
//this是当前类的一个实例p1
this.name=name; //=>p1.name=name
this.age=age; //=>p1.age=age
}
var p1=new CreateJsPerson("尹华芝",48);

// 情况4
function add(c, d){
  return this.a + this.b + c + d;
}
var o = {a:1, b:3};
add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34

// 情况5
<button id="btn1">箭头函数this</button>
<script type="text/javascript">
    let btn1 = document.getElementById('btn1');
    let obj = {
        name: 'kobe',
        age: 39,
        getName: function () {
            btn1.onclick = () => {
                console.log(this);//obj
            };
        }
    };
    obj.getName();
</script>

Arguments 对象

arguments 是一个对应于传递给函数的参数的类数组对象。它只具有length和所以属性。

function fn(a,b,c){
	console.log(arguments[0]);//a
    console.log(arguments[1]);//b
}

把arguments转化成数组

// ES5
var arg1 = Array.prototype.slice.call(arguments);
var arg2 = [].slice.call(arguments);

// ES6
var arg3 = Array.from(arguments);
var arg4 = [...arguments];

链接:https://juejin.cn/post/6893642486556655630

手写call apply bind

1.手写call

首先要明确call的功能是对函数进行绑定,即要先原型上即 Function.prototype 上编程

1)拿到函数的引用this(判断是否是函数)

2)拿到传入的对象content(判断是否是null)

3)content.fn = this

4)获取传入的参数args

5)执行content.fn(...args)

6)删除函数fn,返回结果

//this指向调用mycall的那个函数
//content表示传入的对象
Function.prototype.myCall = function(content){
    //先判断调用者是否是函数
    if(typeof this !== 'function'){
        throw new TypeError("调用者不是一个有效函数");
    }
    //不传参默认为window
    content = content || window;

    //给对象新增一个属性fn 并且指向要调用的函数
    content.fn = this;
    //把传入的参数(第二个以及第二个以后的)转化成数组
    const args = Array.from(arguments).slice(1);
    //对象调用函数
    const result = content.fn(...args);
    //删除函数
    delete content.fn;

    return result;
}

function people(age){
    console.log(this.name + " " + age);
}

let Tom = {
    name:'Tom'
}
people.myCall(Tom,12);//Tom  12

2.手写apply

apply的功能是对函数进行绑定,即要先原型上即 Function.prototype 上编程,它和call的主要区别在于传入的参数是一个数组,所以只需修改上call部分的3)


Function.prototype.myApply = function(content){
    //先判断调用者是否是函数
    if(typeof this !== 'function'){
        throw new TypeError("调用者不是一个有效函数");
    }
    //不传参默认为window
    content = content || window;

    //给对象新增一个属性fn 并且指向要调用的函数
    content.fn = this;

    let result;
    if(arguments[1]){//如果存在参数就是arguments[1]-->参数数组
        result = content.fn(...arguments[1]);
    }else{
        result = content.fn();
    }
    //删除函数
    delete content.fn;

    return result;
}

function people(age1,age2,age3){
    console.log(this.name + " " + age1 + " " + age2 + " " + age3);
}

let Tom = {
    name:'Tom'
}
people.myApply(Tom,[12,13,14]);//Tom  12 12 14

3.手写bind

bind的功能是对原函数this绑定后返回一个新函数,要先原型上即 Function.prototype 上编程

1)拿到函数的引用this(判断是否是函数)

2)content.fn = this

3)获取传入的参数args

4)返回一个新函数result

5)绑定原型链 result.prototype = Object.create(fn.prototype);

 Function.prototype.myBind = function(context){
        //判断函数
        if(typeof this !== 'function'){
            throw new TypeError('Error');
        }
        // fn指向原函数
        const fn = this;
        //拿到参数
        const args = Array.from(arguments).slice(1);

        //返回一个新函数
        const result =  function(){
            const resultArg = [...arguments];

            if(this instanceof result){//如果是通过new调用的,绑定this为实例对象
                fn.apply(this,args.concat(resultArg));
            }else{//普通函数形式绑定
                fn.apply(context,args.concat(resultArg));
            }
        }

        //绑定原型链
        result.prototype = Object.create(fn.prototype);

        return result;
    } 

    function print(){
        console.log(this.name);
    }
    let people = {
        name:"Lily"
    }
    let F = print.myBind(people,1,2,3);
    F();//Lily