찾다
웹 프론트엔드JS 튜토리얼JS에서 이것이 정확히 무엇을 가리키는가?

JS에서 이것이 정확히 무엇을 가리키는가?

Nov 27, 2019 pm 03:56 PM
javascript프런트 엔드

JS에서 이것이 정확히 무엇을 가리키는가?

1. this

JS의 this는 초보자에게 쉽게 혼란을 줄 수 있습니다. 이것은 매우 강력하지만 천천히 이해하려면 약간의 노력이 필요합니다. this 对于咱们的初学者很容易产生困惑不解。 this 的功能很强大,但需要一定付出才能慢慢理解它。

【相关课程推荐:JavaScript视频教程

对Java、PHP或其他标准语言来看,this 表示类方法中当前对象的实例。大多数情况下,this 不能在方法之外使用,这样就比较不会造成混淆。

在J要中情况就有所不同: this表示函数的当前执行上下文,JS 中函数调用主要有以下几种方式:

  • 函数调用: alert('Hello World!')
  • 方法调用: console.log('Hello World!')
  • 构造函数: new RegExp('\d')
  • 隐式调用:  alert.call(undefined, 'Hello World!')

每种调用类型以自己的方式定义上下文,所以就很容易产生混淆。

此外,严格模式也会影响执行上下文。

理解this关键是要清楚的知道函数调用及其如何影响上下文。

本文主要说明函数的调用方式及如何影响 this,并且说明执行上下文的常见陷阱。

在开始之前,先知道几个术语:

调用函数正在执行创建函数体的代码,或者只是调用函数。 例如,parseInt函数调用是parseInt('15')。

  • 函数调用:执行构成函数主体的代码:例如,parseInt函数调用是parseInt('15')
  • 调用的上下文:指 this 在函数体内的值。 例如,map.set('key', 'value')的调用上下文是 map
  • 函数的作用域:是在函数体中可访问的变量、对象和函数的集合。

2、函数调用

当一个表达式为函数接着一个(,一些用逗号分隔的参数以及一个时,函数调用被执行,例如parseInt('18')

函数调用表达式不能是属性方式的调用,如 obj.myFunc(),这种是创建一个方法调用。再如 [1,5].join(',')不是函数调用,而是方法调用,这种区别需要记住哈,很重要滴

函数调用的一个简单示例:

function hello(name) {
  return 'Hello ' + name + '!';
}
// 函数调用
const message = hello('World');
console.log(message); // => 'Hello World!'

hello('World')是函数调用: hello表达式等价于一个函数,跟在它后面的是一对括号以及'World'参数。

一个更高级的例子是IIFE(立即调用的函数表达式)

const message = (function(name) {
   return 'Hello ' + name + '!';
})('World');
console.log(message) // => 'Hello World!'

IIFE也是一个函数调用:第一对圆括号(function(name) {...})是一个表达式,它的计算结果是一个函数对象,后面跟着一对圆括号,圆括号的参数是“World”

2.1、在函数调用中的this

this 在函数调用中是一个全局对象

局对象由执行环境决定。在浏览器中,thiswindow 对象。

JS에서 이것이 정확히 무엇을 가리키는가?

在函数调用中,执行上下文是全局对象。

再来看看下面函数中的上下文又是什么鬼:

function sum(a, b) {
   console.log(this === window); // => true
   this.myNumber = 20; // 将'myNumber'属性添加到全局对象
   return a + b;
}
// sum() is invoked as a function
// sum() 中的 `this` 是一个全局对象(window)
sum(15, 16);     // => 31
window.myNumber; // => 20

在调用sum(15,16)时,JS 自动将this设置为全局对象,在浏览器中该对象是window

this在任何函数作用域(最顶层作用域:全局执行上下文)之外使用,this 表示 window 对象

console.log(this === window); // => true
this.myString = 'Hello World!';
console.log(window.myString); // => 'Hello World!'

<!-- In an html file -->
<script>
   console.log(this === window); // => true
</script>

2.2、严格模式下的函数调用 this 又是什么样的

this 在严格模式下的函数调用中为 undefined

严格模式是在 ECMAScript 5.1中引入的,它提供了更好的安全性和更强的错误检查。

要启用严格模式,函数头部写入use strict 即可。

启用后,严格模式会影响执行上下文,this 在常规函数调用中值为undefined。 与上述情况2.1

【관련 강좌 추천: JavaScript 동영상 튜토리얼]

JS에서 이것이 정확히 무엇을 가리키는가?Java 및 PHP용 또는 다른 표준 언어인 this는 클래스 메서드에서 현재 개체의 인스턴스를 나타냅니다. 대부분의 경우 this는 메소드 외부에서 사용할 수 없으므로 덜 혼란스럽습니다.

J에서는 상황이 다릅니다: this는 함수의 현재 실행 컨텍스트를 나타냅니다. JS에서 함수를 호출하는 방법은 주로 다음과 같습니다.

  • 함수 호출: alert('Hello World!')
  • 메서드 호출: console.log('Hello World!')
  • 생성자: new RegExp('\d')
  • 암시적 호출: alert.call(unjust, 'Hello World!')
  • 🎜각 통화 유형은 고유한 방식으로 컨텍스트를 정의하므로 혼동되기 쉽습니다. 🎜🎜또한 엄격 모드는 실행 컨텍스트에도 영향을 미칩니다. 🎜🎜 이것을 이해하는 열쇠는 함수 호출과 그것이 컨텍스트에 어떤 영향을 미치는지 명확하게 아는 것입니다. 🎜🎜이 문서에서는 주로 함수가 호출되는 방식과 함수가 에 미치는 영향을 설명하고 실행 컨텍스트의 일반적인 함정에 대해 설명합니다. 🎜🎜시작하기 전에 몇 가지 용어를 알아두세요. 🎜🎜함수 호출은 함수 본문을 생성하는 코드를 실행하거나 함수를 호출하는 것입니다. 예를 들어,parseInt 함수 호출은parseInt('15')입니다. 🎜
    • 🎜Function call🎜: 함수 본문을 구성하는 코드를 실행합니다. 예를 들어 parseInt 함수 호출은 parseInt('15')입니다. 코드>.
    • 🎜Call context🎜: 함수 본문의 this 값을 참조합니다. 예를 들어 map.set('key', 'value')의 호출 컨텍스트는 map입니다.
    • 🎜함수 범위🎜: 함수 본문에서 액세스할 수 있는 변수, 개체 및 함수의 모음입니다.
    🎜🎜🎜2. 함수 호출🎜🎜🎜🎜 표현식이 함수 뒤에 (, 쉼표로 구분된 일부 매개변수 및 ) code>이면 <code>parseInt('18')와 같은 함수 호출이 실행됩니다. 🎜🎜함수 호출 표현식은 메서드 호출을 생성하는 obj.myFunc()와 같은 속성 호출일 수 없습니다. 또 다른 예는 [1,5].join(',')이 함수 호출이 아니라 메서드 호출이라는 것입니다. 🎜이 차이점은 기억해야 하며 매우 중요합니다🎜. 🎜🎜함수 호출의 간단한 예: 🎜
    function multiply(a, b) {
      'use strict'; // 启用严格模式
      console.log(this === undefined); // => true
      return a * b;
    }
    multiply(2, 5); // => 10
    🎜hello('World')는 함수 호출입니다. hello 표현식은 함수와 동일하며 그 뒤에는 Is가 옵니다. 한 쌍의 괄호와 'World' 매개변수. 🎜🎜더 고급 예는 🎜IIFE🎜(즉시 호출되는 함수 표현식)입니다.🎜
    function execute() {
       'use strict'; // 开启严格模式  
       function concat(str1, str2) {
         // 严格模式仍然有效  
         console.log(this === undefined); // => true
         return str1 + str2;
       }
       // concat() 在严格模式下作为函数调用
       // this in concat() is undefined
       concat('Hello', ' World!'); // => "Hello World!"
    }
    execute();
    🎜🎜IIFE🎜는 함수 호출이기도 합니다. 첫 번째 쌍의 괄호 (function(name) {...})는 함수 개체로 평가되고 그 뒤에 매개변수가 <code>"World"인 괄호 쌍이 오는 표현식입니다. 🎜🎜🎜2.1.함수 호출의 경우🎜🎜
    함수 호출의 전역 개체입니다.
    🎜전역 개체는 실행 환경에 따라 결정됩니다. 브라우저에서 thiswindow 개체입니다. 🎜🎜JS에서 이것이 정확히 무엇을 가리키는가?🎜🎜at 함수 호출에서 실행 컨텍스트는 전역 개체입니다. 🎜🎜다음 함수의 컨텍스트를 살펴보겠습니다. 🎜
    function nonStrictSum(a, b) {
      // 非严格模式
      console.log(this === window); // => true
      return a + b;
    }
    function strictSum(a, b) {
      'use strict';
      // 启用严格模式
      console.log(this === undefined); // => true
      return a + b;
    }
    
    nonStrictSum(5, 6); // => 11
    strictSum(8, 12); // => 20
    🎜 sum(15,16)을 호출하면 JS는 자동으로 this를 전역 객체로 설정합니다. 브라우저에서 이 개체는 window입니다. 🎜🎜this가 함수 범위(최상위 범위: 전역 실행 컨텍스트) 외부에서 사용되는 경우 thiswindow 개체를 나타냅니다🎜
    const numbers = {
       numberA: 5,
       numberB: 10,
       sum: function() {
         console.log(this === numbers); // => true
         function calculate() {
           console.log(this === numbers); // => false
           return this.numberA + this.numberB;
         }
         return calculate();
       }
    };
    numbers.sum(); // => NaN
    🎜 🎜2.2. 엄격 모드에서 this 함수는 어떤 모습인가요? 🎜🎜
    this 엄격 모드의 함수 호출에서는 🎜undefine🎜
    🎜Strict 모드가 도입되었습니다. ECMAScript 5.1에서는 더 나은 보안과 더 강력한 오류 검사를 제공합니다. 🎜🎜엄격 모드를 활성화하려면 함수 헤더에 use strict를 작성하세요. 🎜🎜활성화되면 엄격 모드는 실행 컨텍스트에 영향을 미치며 일반 함수 호출에서 this 값은 undefine입니다. 위의 2.1 사례와 달리 실행 컨텍스트는 더 이상 전역 개체가 아닙니다. 🎜🎜🎜🎜🎜엄격 모드 함수 호출 예: 🎜
    function multiply(a, b) {
      'use strict'; // 启用严格模式
      console.log(this === undefined); // => true
      return a * b;
    }
    multiply(2, 5); // => 10

    multiply(2,5)作为函数调用时,thisundefined

    严格模式不仅在当前作用域中有效,在内部作用域中也是有效的(对于在内部声明的所有函数):

    function execute() {
       'use strict'; // 开启严格模式  
       function concat(str1, str2) {
         // 严格模式仍然有效  
         console.log(this === undefined); // => true
         return str1 + str2;
       }
       // concat() 在严格模式下作为函数调用
       // this in concat() is undefined
       concat('Hello', ' World!'); // => "Hello World!"
    }
    execute();

    'use strict'被插入到执行体的顶部,在其作用域内启用严格模式。 因为函数concat是在执行的作用域中声明的,所以它继承了严格模式。

    单个JS文件可能包含严格和非严格模式。 因此,对于相同的调用类型,可以在单个脚本中具有不同的上下文行为:

    function nonStrictSum(a, b) {
      // 非严格模式
      console.log(this === window); // => true
      return a + b;
    }
    function strictSum(a, b) {
      'use strict';
      // 启用严格模式
      console.log(this === undefined); // => true
      return a + b;
    }
    
    nonStrictSum(5, 6); // => 11
    strictSum(8, 12); // => 20

    2.3、陷阱:this 在内部函数中的时候

    函数调用的一个常见陷阱是,认为this在内部函数中的情况与外部函数中的情况相同。

    正确地说,内部函数的上下文只依赖于它的调用类型,而不依赖于外部函数的上下文。

    要将 this 设置为所需的值,可以通过 .call().apply()修改内部函数的上下文或使用.bind()创建绑定函数。

    下面的例子是计算两个数的和:

    const numbers = {
       numberA: 5,
       numberB: 10,
       sum: function() {
         console.log(this === numbers); // => true
         function calculate() {
           console.log(this === numbers); // => false
           return this.numberA + this.numberB;
         }
         return calculate();
       }
    };
    numbers.sum(); // => NaN

    sum()是对象上的方法调用,所以sum中的上下文是numbers对象。calculate函数是在sum中定义的,你可能希望在calculate()this也表示number对象。

    calculate()是一个函数调用(不是方法调用),它将this作为全局对象window(非严格模下)。即使外部函数sum将上下文作为number对象,它在calculate里面没有影响。

    sum()的调用结果是NaN,不是预期的结果5 + 10 = 15,这都是因为没有正确调用calculate

    为了解决这个问题,calculate函数中上下文应该与 sum 中的一样,以便可以访问numberAnumberB属性。

    一种解决方案是通过调用calculator.call(this)手动将calculate上下文更改为所需的上下文。

    const numbers = {
       numberA: 5,
       numberB: 10,
       sum: function() {
         console.log(this === numbers); // => true
         function calculate() {
           console.log(this === numbers); // => true
           return this.numberA + this.numberB;
         }
         // 使用 .call() 方法修改上下文
         return calculate.call(this);
       }
    };
    numbers.sum(); // => 15

    call(this)像往常一样执行calculate函数,但 call 会把上下文修改为指定为第一个参数的值。

    现在this.numberA + this.numberB相当于numbers.numberA + numbers.numberB。 该函数返回预期结果5 + 10 = 15

    另一种就是使用箭头函数

    const numbers = {
       numberA: 5,
       numberB: 10,
       sum: function() {
         console.log(this === numbers); // => true
         const calculate = () => {
           console.log(this === numbers); // => true
           return this.numberA + this.numberB;
         }
         return calculate();
       }
    };
    numbers.sum(); // => 15

    3、方法调用

    方法是存储在对象属性中的函数。例如

    const myObject = {
      // helloFunction 是一个方法
      helloFunction: function() {
        return 'Hello World!';
      }
    };
    const message = myObject.helloFunction();

    helloFunctionmyObject的一个方法,要调用该方法,可以这样子调用 :myObject.helloFunction

    当一个表达式以属性访问的形式执行时,执行的是方法调用,它相当于以个函数接着(,一组用逗号分隔的参数以及)。

    利用前面的例子,myObject.helloFunction()是对象myObject上的一个helloFunction的方法调用。[1, 2].join(',')/\s/.test('beautiful world')也被认为是方法调用。

    区分函数调用和方法调用非常重要,因为它们是不同的类型。主要区别在于方法调用需要一个属性访问器形式来调用函数(obj.myFunc()obj['myFunc']()),而函数调用不需要(myFunc())。

    ['Hello', 'World'].join(', '); // 方法调用
    ({ ten: function() { return 10; } }).ten(); // 方法调用
    const obj = {};
    obj.myFunction = function() {
      return new Date().toString();
    };
    obj.myFunction(); // 方法调用
    
    const otherFunction = obj.myFunction;
    otherFunction();     // 函数调用
    parseFloat('16.60'); // 函数调用
    isNaN(0);            // 函数调用

    理解函数调用和方法调用之间的区别有助于正确识别上下文。

    3.1、方法调用中 this 是肿么样

    在方法调用中,this是拥有这个方法的对象

    当调用对象上的方法时,this就变成了对象本身。

    JS에서 이것이 정확히 무엇을 가리키는가?

    创建一个对象,该对象有一个递增数字的方法

    const calc = {
      num: 0,
      increment: function() {
        console.log(this === calc); // => true
        this.num += 1;
        return this.num;
      }
    };
    // method invocation. this is calc
    calc.increment(); // => 1
    calc.increment(); // => 2

    调用calc.increment()使increment函数的上下文成为calc对象。所以使用this.num来增加num属性是有效的。

    再来看看另一个例子。JS对象从原型继承一个方法,当在对象上调用继承的方法时,调用的上下文仍然是对象本身

    const myDog = Object.create({
      sayName: function() {
         console.log(this === myDog); // => true
         return this.name;
      }
    });
    myDog.name = 'Milo';
    // 方法调用 this 指向 myDog
    myDog.sayName(); // => 'Milo'

    Object.create()创建一个新对象myDog,并根据第一个参数设置其原型。myDog对象继承sayName方法。

    执行myDog. sayname()时,myDog是调用的上下文。

    在EC6 class 语法中,方法调用上下文也是实例本身

    class Planet {
      constructor(name) {
        this.name = name;    
      }
      getName() {
        console.log(this === earth); // => true
        return this.name;
      }
    }
    var earth = new Planet('Earth');
    // method invocation. the context is earth
    earth.getName(); // => 'Earth'

    3.2、陷阱:将方法与其对象分离

    方法可以从对象中提取到一个单独的变量const alone = myObj.myMethod。当方法单独调用时,与原始对象alone()分离,你可能认为当前的this就是定义方法的对象myObject

    如果方法在没有对象的情况下调用,那么函数调用就会发生,此时的this指向全局对象window严格模式下是undefined

    下面的示例定义了Animal构造函数并创建了它的一个实例:myCat。然后setTimout()在1秒后打印myCat对象信息

    function Animal(type, legs) {
      this.type = type;
      this.legs = legs;  
      this.logInfo = function() {
        console.log(this === myCat); // => false
        console.log('The ' + this.type + ' has ' + this.legs + ' legs');
      }
    }
    const myCat = new Animal('Cat', 4);
    // The undefined has undefined legs 
    setTimeout(myCat.logInfo, 1000);

    你可能认为setTimout调用myCat.loginfo()时,它应该打印关于myCat对象的信息。

    不幸的是,方法在作为参数传递时与对象是分离,setTimout(myCat.logInfo)以下情况是等效的:

    setTimout(myCat.logInfo);
    // 等价于
    const extractedLogInfo = myCat.logInfo;
    setTimout(extractedLogInfo);

    将分离的logInfo作为函数调用时,this是全局 window,所以对象信息没有正确地打印。

    函数可以使用.bind()方法与对象绑定,就可以解决 this 指向的问题。

    function Animal(type, legs) {
      this.type = type;
      this.legs = legs;  
      this.logInfo = function() {
        console.log(this === myCat); // => true
        console.log('The ' + this.type + ' has ' + this.legs + ' legs');
      };
    }
    const myCat = new Animal('Cat', 4);
    // logs "The Cat has 4 legs"
    setTimeout(myCat.logInfo.bind(myCat), 1000);

    myCat.logInfo.bind(myCat)返回一个新函数,它的执行方式与logInfo完全相同,但是此时的 this 指向 myCat,即使在函数调用中也是如此。

    另一种解决方案是将logInfo()方法定义为一个箭头函数:

    function Animal(type, legs) {
      this.type = type;
      this.legs = legs;  
      this.logInfo = () => {
        console.log(this === myCat); // => true
        console.log('The ' + this.type + ' has ' + this.legs + ' legs');
      };
    }
    const myCat = new Animal('Cat', 4);
    // logs "The Cat has 4 legs"
    setTimeout(myCat.logInfo, 1000);

    4、构造函数调用

    new关键词紧接着函数对象,(,一组逗号分隔的参数以及)时被调用,执行的是构造函数调用如new RegExp('\\d')

    声明了一个Country函数,并且将它作为一个构造函数调用:

    function Country(name, traveled) {
       this.name = name ? name : 'United Kingdom';
       this.traveled = Boolean(traveled); 
    }
    Country.prototype.travel = function() {
      this.traveled = true;
    };
    // 构造函数调用
    const france = new Country('France', false);
    // 构造函数调用
    const unitedKingdom = new Country;
    
    france.travel(); // Travel to France

    new Country('France', false)Country函数的构造函数调用。它的执行结果是一个name属性为'France'的新的对象。 如果这个构造函数调用时不需要参数,那么括号可以省略:new Country

    从ES6开始,JS 允许用class关键词来定义构造函数

    class City {
      constructor(name, traveled) {
        this.name = name;
        this.traveled = false;
      }
      travel() {
        this.traveled = true;
      }
    }
    // Constructor invocation
    const paris = new City('Paris', false);
    paris.travel();

    new City('Paris')是构造函数调用。这个对象的初始化由这个类中一个特殊的方法constructor来处理。其中,this指向新创建的对象。

    构造函数创建了一个新的空的对象,它从构造函数的原型继承了属性。构造函数的作用就是去初始化这个对象。 可能你已经知道了,在这种类型的调用中,上下文指向新创建的实例。

    当属性访问myObject.myFunction前面有一个new关键词时,JS会执行构造函数调用而不是原来的方法调用。

    例如new myObject.myFunction():它相当于先用属性访问把方法提取出来extractedFunction = myObject.myFunction,然后利用把它作为构造函数创建一个新的对象: new extractedFunction()

    4.1、构造函数中的 this

    在构造函数调用中 this 指向新创建的对象

    构造函数调用的上下文是新创建的对象。它利用构造函数的参数初始化新的对象,设定属性的初始值,添加事件处理函数等等。

    JS에서 이것이 정확히 무엇을 가리키는가?

    来看看下面示例中的上下文

    function Foo () {
      console.log(this instanceof Foo); // => true
      this.property = 'Default Value';
    }
    // Constructor invocation
    const fooInstance = new Foo();
    fooInstance.property; // => 'Default Value'

    new Foo() 正在进行构造函数调用,其中上下文是fooInstance。 在Foo内部初始化对象:this.property被赋值为默认值。

    同样的情况在用class语法(从ES6起)时也会发生,唯一的区别是初始化在constructor方法中进行:

    class Bar {
      constructor() {
        console.log(this instanceof Bar); // => true
        this.property = 'Default Value';
      }
    }
    // Constructor invocation
    const barInstance = new Bar();
    barInstance.property; // => 'Default Value'

    4.2、陷阱: 忘了使用 new

    有些JS函数不是只在作为构造函数调用的时候才创建新的对象,作为函数调用时也会,例如RegExp

    var reg1 = new RegExp('\\w+');
    var reg2 = RegExp('\\w+');
    
    reg1 instanceof RegExp;      // => true
    reg2 instanceof RegExp;      // => true
    reg1.source === reg2.source; // => true

    当执行的 new RegExp('\\w+')RegExp('\\w+')时,JS 会创建等价的正则表达式对象。

    使用函数调用来创建对象存在一个潜在的问题(不包括工厂模式),因为一些构造函数可能会忽略在缺少new关键字时初始化对象的逻辑。

    下面的例子说明了这个问题:

    function Vehicle(type, wheelsCount) {
      this.type = type;
      this.wheelsCount = wheelsCount;
      return this;
    }
    // 忘记使用 new 
    const car = Vehicle('Car', 4);
    car.type;       // => 'Car'
    car.wheelsCount // => 4
    car === window  // => true

    Vehicle是一个在上下文对象上设置typewheelsCount属性的函数。

    当执行Vehicle('Car', 4)时,返回一个对象Car,它具有正确的属性:Car.typeCarCar.wheelsCount4,你可能认为它很适合创建和初始化新对象。

    然而,在函数调用中,thiswindow对象 ,因此 Vehicle('Car',4)window 对象上设置属性。 显然这是错误,它并没有创建新对象。

    当你希望调用构造函数时,确保你使用了new操作符:

    function Vehicle(type, wheelsCount) {
      if (!(this instanceof Vehicle)) {
        throw Error('Error: Incorrect invocation');
      }
      this.type = type;
      this.wheelsCount = wheelsCount;
      return this;
    }
    // Constructor invocation
    const car = new Vehicle('Car', 4);
    car.type               // => 'Car'
    car.wheelsCount        // => 4
    car instanceof Vehicle // => true
    
    // Function invocation. Throws an error.
    const brokenCar = Vehicle('Broken Car', 3);

    new Vehicle('Car',4) 运行正常:创建并初始化一个新对象,因为构造函数调用中时使用了new关键字。

    在构造函数里添加了一个验证this instanceof Vehicle来确保执行的上下文是正确的对象类型。如果this不是Vehicle,那么就会报错。这样,如果执行Vehicle('Broken Car', 3)(没有new),我们会得到一个异常:Error: Incorrect invocation

    5、隐式调用

    使用myFun.call()myFun.apply()方法调用函数时,执行的是隐式调用。

    JS中的函数是第一类对象,这意味着函数就是对象,对象的类型为Function。从函数对象的方法列表中,.call().apply()用于调用具有可配置上下文的函数。

    • 方法 .call(thisArg[, arg1[, arg2[, ...]]])将接受的第一个参数thisArg作为调用时的上下文,arg1, arg2, ...这些则作为参数传入被调用的函数。
    • 方法.apply(thisArg, [args])将接受的第一个参数thisArg作为调用时的上下文,并且接受另一个类似数组的对象[arg1, arg2, ...] 作为被调用函数的参数传入。

    下面是隐式调用的例子

    function increment(number) {
      return ++number;  
    }
    increment.call(undefined, 10);    // => 11
    increment.apply(undefined, [10]); // => 11

    increment.call()increment.apply()都用参数10调用了这个自增函数。

    两者的区别是.call()接受一组参数,例如myFunction.call(thisValue, 'value1', 'value2')。而.apply()接受的一组参数必须是一个类似数组的对象,例如myFunction.apply(thisValue, ['value1', 'value2'])。

    5.1、隐式调用中的this

    在隐式调用.call()或.apply()中,this是第一个参数

    很明显,在隐式调用中,this作为第一个参数传递给.call().apply()

    var rabbit = { name: 'White Rabbit' };
    function concatName(string) {
      console.log(this === rabbit); // => true
      return string + this.name;
    }
    concatName.call(rabbit, 'Hello ');  // => 'Hello White Rabbit'
    concatName.apply(rabbit, ['Bye ']); // => 'Bye White Rabbit'

    当应该使用特定上下文执行函数时,隐式调用非常有用。例如为了解决方法调用时,this总是window或严格模式下的undefined的上下文问题。隐式调用可以用于模拟在一个对象上调用某个方法。

    function Runner(name) {
      console.log(this instanceof Rabbit); // => true
      this.name = name;  
    }
    function Rabbit(name, countLegs) {
      console.log(this instanceof Rabbit); // => true
      Runner.call(this, name);
      this.countLegs = countLegs;
    }
    const myRabbit = new Rabbit('White Rabbit', 4);
    myRabbit; // { name: 'White Rabbit', countLegs: 4 }

    Rabbit中的Runner.call(this, name)隐式调用了父类的函数来初始化这个对象。

    6、绑定函数

    绑定函数是与对象连接的函数。通常使用.bind()方法从原始函数创建。原始函数和绑定函数共享相同的代码和作用域,但执行时上下文不同。

    方法 myFunc.bind(thisArg[, arg1[, arg2[, ...]]])接受第一个参数thisArg作为绑定函数执行时的上下文,并且它接受一组可选的参数 arg1, arg2, ...作为被调用函数的参数。它返回一个绑定了thisArg的新函数。

    function multiply(number) {
      'use strict';
      return this * number;
    }
    const double = multiply.bind(2);
    
    double(3);  // => 6
    double(10); // => 20

    bind(2)返回一个新的函数对象doubledouble 绑定了数字2multiplydouble具有相同的代码和作用域。

    .apply().call() 方法相反,它不会立即调用该函数,.bind()方法只返回一个新函数,在之后被调用,只是this已经被提前设置好了。

    6.1、绑定函数中的this

    在调用绑定函数时,this.bind()的第一个参数。

    .bind()的作用是创建一个新函数,调用该函数时,将上下文作为传递给.bind()的第一个参数。它是一种强大的技术,使咱们可以创建一个定义了this值的函数。

    JS에서 이것이 정확히 무엇을 가리키는가?

    来看看,如何在如何在绑定函数设置 this

    const numbers = {
      array: [3, 5, 10],
      getNumbers: function() {
        return this.array;    
      }
    };
    const boundGetNumbers = numbers.getNumbers.bind(numbers);
    boundGetNumbers(); // => [3, 5, 10]
    // Extract method from object
    const simpleGetNumbers = numbers.getNumbers;
    simpleGetNumbers(); // => undefined (严格模式下报错)

    numbers.getNumbers.bind(numbers)返回绑定numbers对象boundGetNumbers函数。boundGetNumbers()调用时的thisnumber对象,并能够返回正确的数组对象。

    可以将函数numbers.getNumbers提取到变量simpleGetNumbers中而不进行绑定。在之后的函数调用中simpleGetNumbers()thiswindow(严格模式下为undefined),不是number对象。在这个情况下,simpleGetNumbers()不会正确返回数组。

    6.2、紧密的上下文绑定

    .bind()创建一个永久的上下文链接,并始终保持它。 一个绑定函数不能通过.call()或者.apply()来改变它的上下文,甚至是再次绑定也不会有什么作用。

    只有绑定函数的构造函数调用才能更改已经绑定的上下文,但是很不推荐的做法(构造函数调用必须使用常规的非绑定函数)。

    下面示例创建一个绑定函数,然后尝试更改其已预先定义好的上下文

    function getThis() {
      'use strict';
      return this;
    }
    const one = getThis.bind(1);
    // 绑定函数调用
    one(); // => 1
    // 使用带有.apply()和.call()的绑定函数
    one.call(2);  // => 1
    one.apply(2); // => 1
    // 再次绑定
    one.bind(2)(); // => 1
    // 以构造函数的形式调用绑定函数
    new one(); // => Object

    只有new one()改变了绑定函数的上下文,其他方式的调用中this总是等于1。

    7、箭头函数

    箭头函数用于以更短的形式声明函数,并在词法上绑定上下文。它可以这样使用

    const hello = (name) => {
      return 'Hello ' + name;
    };
    hello('World'); // => 'Hello World'
    // Keep only even numbers
    [1, 2, 5, 6].filter(item => item % 2 === 0); // => [2, 6]

    箭头函数语法简单,没有冗长的function 关键字。当箭头函数只有一条语句时,甚至可以省略return关键字。

    箭头函数是匿名的,这意味着name属性是一个空字符串''。这样它就没有词法上函数名(函数名对于递归、分离事件处理程序非常有用)

    同时,跟常规函数相反,它也不提供arguments对象。但是,这在ES6中通过rest parameters修复了:

    const sumArguments = (...args) => {
       console.log(typeof arguments); // => 'undefined'
       return args.reduce((result, item) => result + item);
    };
    sumArguments.name      // => ''
    sumArguments(5, 5, 6); // => 16

    7.1、箭头函数中的this

    this 定义箭头函数的封闭上下文

    箭头函数不会创建自己的执行上下文,而是从定义它的外部函数中获取 this。 换句话说,箭头函数在词汇上绑定 this

    JS에서 이것이 정확히 무엇을 가리키는가?

    下面的例子说明了这个上下文透明的特性:

    class Point {
      constructor(x, y) {
        this.x = x;
        this.y = y;
      }
      log() {
        console.log(this === myPoint); // => true
        setTimeout(()=> {
          console.log(this === myPoint);      // => true
          console.log(this.x + ':' + this.y); // => '95:165'
        }, 1000);
      }
    }
    const myPoint = new Point(95, 165);
    myPoint.log();

    setTimeout使用与log()方法相同的上下文(myPoint对象)调用箭头函数。正如所见,箭头函数从定义它的函数继承上下文。

    如果在这个例子里尝试用常规函数,它创建自己的上下文(window或严格模式下的undefined)。因此,要使相同的代码正确地使用函数表达式,需要手动绑定上下文:setTimeout(function(){…}.bind(this))。这很冗长,使用箭头函数是一种更简洁、更短的解决方案。

    如果箭头函数在最顶层的作用域中定义(在任何函数之外),则上下文始终是全局对象(浏览器中的 window):

    onst getContext = () => {
       console.log(this === window); // => true
       return this;
    };
    console.log(getContext() === window); // => true

    箭头函数一劳永逸地与词汇上下文绑定。 即使修改上下文,this也不能被改变:

    const numbers = [1, 2];
    (function() {  
      const get = () => {
        console.log(this === numbers); // => true
        return this;
      };
      console.log(this === numbers); // => true
      get(); // => [1, 2]
      // Use arrow function with .apply() and .call()
      get.call([0]);  // => [1, 2]
      get.apply([0]); // => [1, 2]
      // Bind
      get.bind([0])(); // => [1, 2]
    }).call(numbers);

    无论如何调用箭头函数get,它总是保留词汇上下文numbers。 用其他上下文的隐式调用(通过 get.call([0])get.apply([0]))或者重新绑定(通过.bind())都不会起作用。

    箭头函数不能用作构造函数。 将它作为构造函数调用(new get())会抛出一个错误:TypeError: get is not a constructor

    7.2、陷阱: 用箭头函数定义方法

    你可能希望使用箭头函数来声明一个对象上的方法。箭头函数的定义相比于函数表达式短得多:(param) => {...} instead of function(param) {..}

    来看看例子,用箭头函数在Period类上定义了format()方法:

    function Period (hours, minutes) {  
      this.hours = hours;
      this.minutes = minutes;
    }
    Period.prototype.format = () => {
      console.log(this === window); // => true
      return this.hours + ' hours and ' + this.minutes + ' minutes';
    };
    const walkPeriod = new Period(2, 30);  
    walkPeriod.format(); // => 'undefined hours and undefined minutes'

    由于format是一个箭头函数,并且在全局上下文(最顶层的作用域)中定义,因此 this 指向window对象。

    即使format作为方法在一个对象上被调用如walkPeriod.format()window仍然是这次调用的上下文。之所以会这样是因为箭头函数有静态的上下文,并不会随着调用方式的改变而改变。

    该方法返回'undefined hours和undefined minutes',这不是咱们想要的结果。

    函数表达式解决了这个问题,因为常规函数确实能根据实际调用改变它的上下文:

    function Period (hours, minutes) {  
      this.hours = hours;
      this.minutes = minutes;
    }
    Period.prototype.format = function() {
      console.log(this === walkPeriod); // => true
      return this.hours + ' hours and ' + this.minutes + ' minutes';
    };
    const walkPeriod = new Period(2, 30);  
    walkPeriod.format(); // => '2 hours and 30 minutes'

    walkPeriod.format()是一个对象上的方法调用,它的上下文是walkPeriod对象。this.hours等于2this.minutes等于30,所以这个方法返回了正确的结果:'2 hours and 30 minutes'

    总结

    为函数调用对this影响最大,从现在开始不要问自己:

    this 是从哪里来的?

    而是要看看

    函数是怎么被调用的?

    对于箭头函数,需要想想

    在这个箭头函数被定义的地方,this是什么?

    这是处理this时的正确想法,它们可以让你免于头痛。

    原文地址:https://dmitripavlutin.com/gentle-explanation-of-this-in-javascript/

    为了保证的可读性,本文采用意译而非直译。

    本文来自 js教程 栏目,欢迎学习!

위 내용은 JS에서 이것이 정확히 무엇을 가리키는가?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명
이 기사는 cnblogs에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제
무대 뒤에서 : 어떤 언어의 힘이 자바 스크립트입니까?무대 뒤에서 : 어떤 언어의 힘이 자바 스크립트입니까?Apr 28, 2025 am 12:01 AM

JavaScript는 브라우저 및 Node.js 환경에서 실행되며 JavaScript 엔진을 사용하여 코드를 구문 분석하고 실행합니다. 1) 구문 분석 단계에서 초록 구문 트리 (AST)를 생성합니다. 2) 컴파일 단계에서 AST를 바이트 코드 또는 기계 코드로 변환합니다. 3) 실행 단계에서 컴파일 된 코드를 실행하십시오.

파이썬과 자바 스크립트의 미래 : 트렌드와 예측파이썬과 자바 스크립트의 미래 : 트렌드와 예측Apr 27, 2025 am 12:21 AM

Python 및 JavaScript의 미래 추세에는 다음이 포함됩니다. 1. Python은 과학 컴퓨팅 분야에서의 위치를 ​​통합하고 AI, 2. JavaScript는 웹 기술의 개발을 촉진하고, 3. 교차 플랫폼 개발이 핫한 주제가되고 4. 성능 최적화가 중점을 둘 것입니다. 둘 다 해당 분야에서 응용 프로그램 시나리오를 계속 확장하고 성능이 더 많은 혁신을 일으킬 것입니다.

Python vs. JavaScript : 개발 환경 및 도구Python vs. JavaScript : 개발 환경 및 도구Apr 26, 2025 am 12:09 AM

개발 환경에서 Python과 JavaScript의 선택이 모두 중요합니다. 1) Python의 개발 환경에는 Pycharm, Jupyternotebook 및 Anaconda가 포함되어 있으며 데이터 과학 및 빠른 프로토 타이핑에 적합합니다. 2) JavaScript의 개발 환경에는 Node.js, VScode 및 Webpack이 포함되어 있으며 프론트 엔드 및 백엔드 개발에 적합합니다. 프로젝트 요구에 따라 올바른 도구를 선택하면 개발 효율성과 프로젝트 성공률이 향상 될 수 있습니다.

JavaScript가 C로 작성 되었습니까? 증거를 검토합니다JavaScript가 C로 작성 되었습니까? 증거를 검토합니다Apr 25, 2025 am 12:15 AM

예, JavaScript의 엔진 코어는 C로 작성되었습니다. 1) C 언어는 효율적인 성능과 기본 제어를 제공하며, 이는 JavaScript 엔진 개발에 적합합니다. 2) V8 엔진을 예를 들어, 핵심은 C로 작성되며 C의 효율성 및 객체 지향적 특성을 결합하여 C로 작성됩니다.

JavaScript의 역할 : 웹 대화식 및 역동적 인 웹JavaScript의 역할 : 웹 대화식 및 역동적 인 웹Apr 24, 2025 am 12:12 AM

JavaScript는 웹 페이지의 상호 작용과 역학을 향상시키기 때문에 현대 웹 사이트의 핵심입니다. 1) 페이지를 새로 고치지 않고 콘텐츠를 변경할 수 있습니다. 2) Domapi를 통해 웹 페이지 조작, 3) 애니메이션 및 드래그 앤 드롭과 같은 복잡한 대화식 효과를 지원합니다. 4) 성능 및 모범 사례를 최적화하여 사용자 경험을 향상시킵니다.

C 및 JavaScript : 연결이 설명되었습니다C 및 JavaScript : 연결이 설명되었습니다Apr 23, 2025 am 12:07 AM

C 및 JavaScript는 WebAssembly를 통한 상호 운용성을 달성합니다. 1) C 코드는 WebAssembly 모듈로 컴파일되어 컴퓨팅 전력을 향상시키기 위해 JavaScript 환경에 도입됩니다. 2) 게임 개발에서 C는 물리 엔진 및 그래픽 렌더링을 처리하며 JavaScript는 게임 로직 및 사용자 인터페이스를 담당합니다.

웹 사이트에서 앱으로 : 다양한 JavaScript 애플리케이션웹 사이트에서 앱으로 : 다양한 JavaScript 애플리케이션Apr 22, 2025 am 12:02 AM

JavaScript는 웹 사이트, 모바일 응용 프로그램, 데스크탑 응용 프로그램 및 서버 측 프로그래밍에서 널리 사용됩니다. 1) 웹 사이트 개발에서 JavaScript는 HTML 및 CSS와 함께 DOM을 운영하여 동적 효과를 달성하고 jQuery 및 React와 같은 프레임 워크를 지원합니다. 2) 반응 및 이온 성을 통해 JavaScript는 크로스 플랫폼 모바일 애플리케이션을 개발하는 데 사용됩니다. 3) 전자 프레임 워크를 사용하면 JavaScript가 데스크탑 애플리케이션을 구축 할 수 있습니다. 4) node.js는 JavaScript가 서버 측에서 실행되도록하고 동시 요청이 높은 높은 요청을 지원합니다.

Python vs. JavaScript : 사용 사례 및 응용 프로그램 비교Python vs. JavaScript : 사용 사례 및 응용 프로그램 비교Apr 21, 2025 am 12:01 AM

Python은 데이터 과학 및 자동화에 더 적합한 반면 JavaScript는 프론트 엔드 및 풀 스택 개발에 더 적합합니다. 1. Python은 데이터 처리 및 모델링을 위해 Numpy 및 Pandas와 같은 라이브러리를 사용하여 데이터 과학 및 기계 학습에서 잘 수행됩니다. 2. 파이썬은 간결하고 자동화 및 스크립팅이 효율적입니다. 3. JavaScript는 프론트 엔드 개발에 없어서는 안될 것이며 동적 웹 페이지 및 단일 페이지 응용 프로그램을 구축하는 데 사용됩니다. 4. JavaScript는 Node.js를 통해 백엔드 개발에 역할을하며 전체 스택 개발을 지원합니다.

See all articles

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

Video Face Swap

Video Face Swap

완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

뜨거운 도구

에디트플러스 중국어 크랙 버전

에디트플러스 중국어 크랙 버전

작은 크기, 구문 강조, 코드 프롬프트 기능을 지원하지 않음

SublimeText3 영어 버전

SublimeText3 영어 버전

권장 사항: Win 버전, 코드 프롬프트 지원!

Dreamweaver Mac版

Dreamweaver Mac版

시각적 웹 개발 도구

WebStorm Mac 버전

WebStorm Mac 버전

유용한 JavaScript 개발 도구

SecList

SecList

SecLists는 최고의 보안 테스터의 동반자입니다. 보안 평가 시 자주 사용되는 다양한 유형의 목록을 한 곳에 모아 놓은 것입니다. SecLists는 보안 테스터에게 필요할 수 있는 모든 목록을 편리하게 제공하여 보안 테스트를 더욱 효율적이고 생산적으로 만드는 데 도움이 됩니다. 목록 유형에는 사용자 이름, 비밀번호, URL, 퍼징 페이로드, 민감한 데이터 패턴, 웹 셸 등이 포함됩니다. 테스터는 이 저장소를 새로운 테스트 시스템으로 간단히 가져올 수 있으며 필요한 모든 유형의 목록에 액세스할 수 있습니다.