JS 中this的指向

1、方法调用模式

一个函数被保存在为对象的一个属性的时,我们称它为一个方法,当一个方法被调用时,this被绑定到该对象;


var name="全局name";
var a ={
    name:"a的name",
    newName:this.name,
    b:function(){
        console.log("你好");
        console.log(this);//指向a {name: 'a的name', newName: '全局name', b: ƒ}
        console.log("这里是:",this.name);//a的name
        console.log(this.newName);//全局name
        this.name="Hello"
        var func = function(){
            console.log(this);//当前this指向window
            console.log(this.name);//全局name
        };
       func();//函数上下文是window,所以这是this 指向 window
    }
}
a.b();//你好

func()这个函数执行的时候他的函数上下文为window。

一:通过赋值的方式改变this的指向


var a ={
    b:function(){
        console.log("你好");
        let _this=this
        _this.name="Hello"
        var func = function(){
           _this.name="HelloTwo"
            console.log(_this.name);//js 就近原则。没有再向上查找 HelloTwo
            console.log(_this);//当前this 指向a
        };
       func()
    }
}
a.b();//你好

2.函数调用模式

当函数并非一个对象的属性时,那么它被当做一个函数来调用,this被绑定到全局对象(因为这个时候foo这个函数的上下文是window绑定的,this就指向window);


var object ={value:100};
object.geValue= function(){
    console.log(this.value);//this 指向 object
    var _this=this;//把object 对象赋值给变量_this
    var fun =function(value){
        this.value=value
        console.log(this);//函数调用,this 指向函数上下文window
        console.log(this.value);//12
        console.log(_this.value)//100 
    }
    fun(12);
    return this.value
}
console.log(object.geValue());//没有设置return 返回undefined,设置了就返回 100

3.构造器中的this:值向新的函数

js中,我们通过new关键词来调用构造函数,此时this会绑定在该对象上


function start(name){
    this.name=name
}
start("王牌");
console.log(this.name);//王牌 console.log(window.name) this 指向window
var starts= new start("王炸");
console.log(starts.name);//王炸 指向 starts

.bind()

用法

1. 用于绑定this

var obj = {
    name: 'obj',
    say: function () { // 这里的this指向obj
        console.log(this.name); // obj
    }
}
var obj2 = {
    name: 'obj2'
}
obj.say(); // obj 
obj.say.bind(obj2)(); // obj2

2. 用于绑定参数和多个参数

var obj = {
    name: 'obj',
    say: function (age) {
        console.log(this.name + ' ' + age);
    }
}
var obj2 = {
    name: 'obj2'
}
obj.say(18); // obj 18
obj.say.bind(obj2, 20)(); // obj2 20

// 多个
var obj = {
    name: 'obj',
    say: function (age, event) {
        console.log(this.name + ' ' + age + ' ' + event.target);
    }
}
var obj2 = {
    name: 'obj2'
}
document.body.addEventListener('click', obj.say.bind(obj2, 20)); // obj2 20 body
document.body.addEventListener('click', obj.say.bind(obj, 20)); // obj 20 body
document.body.addEventListener('click', obj.say.bind(obj, 20, event)); // obj 20 body
document.body.addEventListener('click', obj.say.bind(obj, event, 20)); // obj [object MouseEvent] 20

3. 用于绑定事件对象

var obj = {
    name: 'obj',
    say: function (event) {  // 这里的this指向obj, event指向事件对象, 但是bind方法会将event放在参数列表的最后
        console.log(event.target); // body
    }
}
var obj2 = {
    name: 'obj2'
}
document.body.addEventListener('click', obj.say); // body
document.body.addEventListener('click', obj.say.bind(obj2)); // body
document.body.addEventListener('click', obj.say.bind(obj)); // body
document.body.addEventListener('click', obj.say.bind(obj, event)); // body

.bind() 指向和call一样,都是改变this指向,但是不会立即执行,而是返回一个函数,等待调用。

function.bind(thisArg[, arg1[, arg2[, ...]]])
thisArg:当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用new 操作符调用绑定函数时,该参数无效。

arg1, arg2, ...:当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。

 返回值:返回一个原函数的拷贝,并拥有指定的 this 值和初始参数。

 说明:bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。

另外,bind() 函数会创建一个新函数,新函数的 this 会被绑定到 bind() 的第一个参数。

bind() 除了this 还接受其他参数,bind() 返回的函数也接受参数,这两部分的参数都会按照顺序在函数调用时拼接起来。

bind() 最简单的用法是创建一个函数,不论怎么调用,这个函数都有同样的 this 值。

call()

call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

call(thisArg, arg1, arg2, ...)

会立即执行,bind() 方法接收完参数后会返回一个函数。call() 不会。

thisArg(可选的):在fun函数运行时指定的this值。需要注意的是,指定的this值并不一定是该函数执行时真正的this值,如果这个函数处于非严格模式下,则指定为null和undefined的this值会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象。

arg1, arg2, ...:指定的参数列表。

返回值:使用调用者提供的 this 值和参数调用该函数的返回值。若该方法没有返回值,则返回 undefined。

说明:

call() 允许为不同的对象分配和调用属于一个对象的函数/方法。

call() 提供新的 this 值给当前调用的函数/方法。你可以使用 call 来实现继承:写一个方法,然后让另外一个新的对象来继承它(而不是在新对象中再写一次这个方法)。

 注意:call() 方法的作用和 apply() 方法类似,只有一个区别,就是 call() 方法接受的是若干个参数的列表,而 apply() 方法接受的是一个包含多个参数的数组。

使用 call 方法调用父构造函数

 function Food(name) {
      this.name = name;
    }
    Food.prototype.getName = function() {
      return this.name;
    }
    function Product(name, price) {
      this.name = name;
      this.price = price;
    }
    Product.prototype.getPrice = function() {
      return this.price;
    }
    function FoodProduct(name, price) {
      Food.call(this, name);
      Product.call(this, name, price);
}
    FoodProduct.prototype = Object.create(Product.prototype);
    FoodProduct.prototype.constructor = FoodProduct;
    var cheese = new FoodProduct('feta', 5);
    console.log(cheese.getName()); // feta
    console.log(cheese.getPrice()); // 5

构造函数 Product 属于 Food 的子类。

Product 有两个参数,name 和 price,Food 有一个参数 name

使用 call 方法,我们可以在 Food 的上下文中调用 Product,传递 Food 的 this 作为 Product 的 this。这让我们拥有了在 Food 中设置 name 的能力,通过继承 Product,我们可以重用 Product 的方法设置 price。

使用 call 方法调用匿名函数

  var animals = [
      { species: 'Lion', name: 'King' },
      { species: 'Whale', name: 'Fail' }
    ];
    for (var i = 0; i < animals.length; i++) {
      (function(i) {//这是一个 IIFE,一个立即调用的函数表达式。使用匿名函数来保存 i 的值
        this.print = function() {
          console.log('#' + i + ' ' + this.species
                      + ': ' + this.name);//这里的 this 指向全局对象,即 window。
        }
        this.print(); // 调用匿名函数。
      }).call(animals[i], i); // 使用 call 方法来调用匿名函数。并且指定 this 的值为 animals[i]。i 作为参数传递给匿名函数。
    }

call 方法调用函数并且指定上下文的 'this'

    function greet() { 
      var reply = [this.animal, 'typically sleep between', this.sleepDuration].join(' '); // this 的值在运行时绑定,这里的this指向obj
      console.log(reply);
    }
    var obj = {
      animal: 'cats', sleepDuration: '12 and 16 hours'
    };
    greet.call(obj);  // cats typically sleep between 12 and 16 hours
    //obj作为greet函数的this指向,并且传入参数,执行greet函数,并且返回结果,这里的this指向obj

使用 call 方法调用函数并且不提供参数

var sData = 'Wisen';
    function display() {
      console.log('sData value is %s ', this.sData);
    }
    display.call();  // sData value is Wisen.

使用 call 方法调用函数并且提供参数

    var sData = 'Wisen';
    function display(nm) {
      console.log('sData value is %s ', nm, this.sData);
    }
    display.call(this, 'hello');  // sData value is hello Wisen

使用 call 方法调用函数并且提供另一个带有 call 方法的函数作为参数

 function sayHello() {
      console.log('Hello ' + this.name);
    }
    var person = {
      name: '张三'
    };
    sayHello.call(person);  // Hello 张三。this 指向 person 对象, 传递参数作为后续参数

使用场景

1.改变this指向

function fn1() {
    console.log(this)  // { a: 1 } 没有使用call方法之前this指向window
}
fn1.call({ a: 1 })

2.继承

function Parent() {
    this.name = 'parent'
}
function Child() {
    Parent.call(this) //this指向Child Child继承了Parent的属性,但是没有继承原型上的属性,所以Child.prototype.constructor = Child
    this.type = 'child' 
}
let child = new Child() //因为Child继承了Parent的属性,所以child.name = 'parent'
console.log(child)  // Child {name: "parent", type: "child"}

3.借用方法

let arr1 = [1, 2, 3]
let arr2 = [4, 5, 6]
//Array.prototype.push.call是借用Array.prototype.push方法.
Array.prototype.push.call(arr1, ...arr2) //这里的this指向arr1,相当于arr1.push(...arr2),但是arr1的长度不会改变,因为call只是借用了push方法
console.log(arr1)  // [1, 2, 3, 4, 5, 6]

4.调用函数

function fn2(a, b, c) {
    console.log(a, b, c)  // 1 2 3 
}
fn2.call(null, 1, 2, 3) // 第一个参数为null或者undefined时,this指向window

5.延迟执行

function fn3() {
    console.log(this)  // { a: 1 }
}
setTimeout(fn3.call({ a: 1 }), 1000) //这里使用 call的好处是可以传参(立即执行)
如果使用apply,则需要将参数放在数组中,如下
setTimeout(fn3.apply({ a: 1 }, [1, 2, 3]), 1000);(不会立即执行)

// 不可以不是哟call。因为setTimeout的第一个参数必须是函数,而不是函数的返回值

6.函数柯里化

function fn4(a, b, c) {
    return a + b + c
}
function curry(fn) {
    let args = [].slice.call(arguments, 1) // [1, 2] [].slice.call(arguments, 1)相当于Array.prototype.slice.call(arguments, 1),arguments是一个类数组对象,没有slice方法,所以要借用Array.prototype.slice.call(arguments, 1)来调用slice方法
    return function () { // 返回一个函数,这个函数中的arguments是一个类数组对象,没有slice方法,所以要借用Array.prototype.slice.call(arguments)来调用slice方法
        let newArgs = args.concat([].slice.call(arguments)) // [1, 2].concat([3])相当于[1, 2, 3] args是一个数组,所以可以直接调用concat方法
        return fn.apply(this, newArgs) // fn.apply(this, newArgs)相当于fn(1, 2, 3) apply方法的第二个参数是一个数组 
    }
}

let fn5 = curry(fn4, 1, 2)
console.log(fn5(3))  // 6

7.判断数据类型

function getType(obj) {
    return Object.prototype.toString.call(obj).slice(8, -1) ;//返回类型的字符串,如Array,Object,Number .slice(8, -1)截取字符串,
}
console.log(getType([]))  // Array
console.log(getType({}))  // Object
console.log(getType(1))  // Number
console.log(getType(''))  // String
console.log(getType(true))  // Boolean
console.log(getType(null))  // Null
console.log(getType(undefined))  // Undefined
console.log(getType(Symbol()))  // Symbol
console.log(getType(function () { }))  // Function

8.数组去重

Set是一种新的数据结构,类似于数组,但是成员的值都是唯一的,没有重复的值。

 Set本身是一个构造函数,用来生成Set数据结构。

跟call,apply一样,Set函数可以接受一个数组(或者具有iterable接口的其他数据结构)作为参数,用来初始化

let arr4 = [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
let arr5 = Array.from(new Set(arr4))
console.log(arr5)  // [1, 2, 3, 4, 5]

9.数组扁平化

let arr6 = [1, [2, [3, [4, 5]]], 6]
let arr7 = arr6.flat(Infinity);// Infinity表示无穷大,可以展开任意深度的嵌套数组,但是性能不好,可以使用递归来实现,或者使用正则,或者使用reduce,或者使用扩展运算符
console.log(arr7)  // [1, 2, 3, 4, 5, 6]

10.数组中位数

let arr12 = [1, 2, 3, 4, 5]
let len = arr12.length
let mid = len % 2 === 0 ? (arr12[len / 2] + arr12[len / 2 - 1]) / 2 : arr12[(len - 1) / 2];//奇数个数时,中位数为中间的数,偶数个数时,中位数为中间两个数的平均值
console.log(mid)  // 3

apply

apply和call的区别在于传参的方式不同, apply传参是数组, call传参是一个一个的传

数组中的最大差值

let arr13 = [1, 2, 3, 4, 5]
let max = Math.max.apply(null, arr13) // let max = Math.max(...arr13) 也可以 ,apply和...的区别:apply接收数组,...接收数组或者多个参数,apply的性能比...好,因为...会展开数组
let min = Math.min.apply(null, arr13)
let diff = max - min
console.log(diff)  // 4

获取数组中的最大值和最小值

let max = Math.max(...arr3) 也可以 ,apply和...的区别:apply接收数组,...接收数组或者多个参数,apply的性能比...好,因为...会展开数组

let arr3 = [1, 2, 3, 4, 5]
let max = Math.max.apply(null, arr3)
let min = Math.min.apply(null, arr3)
console.log(max, min)  // 5 1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值