首页 > 编程语言 >【你不知道的JavaScript】this关键字

【你不知道的JavaScript】this关键字

时间:2023-11-15 17:35:07浏览次数:45  
标签:function 绑定 bar JavaScript 关键字 obj2 var foo 知道

没有this时,需要传入上下文获取name,在多个上下文时,代码变得繁杂重复

var me = {
    name: "Kyle"
};
var you = {
    name: "Reader"
};

function identify(context) {
    return context.name.toUpperCase();
}
function speak(context) {
    var greeting = "Hello, I'm " + identify (context);
    console.log(greeting);
}

identify(you); // READER
speak(me); //hello, I'm KYLE

有了this后,代码复用性增加,且更加简洁

function identify() {
    return this.name.toUpperCase();
}
function speak() {
    var greeting = "Hello, I'm " + identify.call(this);
    console.log(greeting);
}

this是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。this绑定方式包括默认绑定、隐式绑定、显式绑定(硬绑定)、new操作符绑定。

// 例1 默认绑定
function foo() {
    console.log(this.a);
}
var a = 2;
foo(); // 2 ,foo在全局作用域调用,this指向全局对象,在全局对象上找a

// 例2 默认绑定
function foo() {
    console.log(this.a);
}
var obj = {
    a: 2,
    foo: foo
};
obj.foo(); // 2 ,foo的上下文对象变为obj,其this指向obj

// 例3 隐式绑定
function foo() {
    console.log(this.a);
}
var obj2 = {
    a: 42,
    foo: foo
};
var obj1 = {
    a: 2,
    obj2: obj2
};
obj1.obj2.foo(); // 42 ,foo的上下文对象是obj2,其this指向obj2

// 例4 隐式绑定
function foo() {
    console.log(this.a);
}
var obj = {
    a: 2,
    foo: foo
};
var bar = obj.foo; // 函数别名!
var a = "oops, global"; // a是全局对象的属性
bar(); // "oops, global" ,bar引用的是foo函数本身,因此此时的bar()其实是一个不带任何修饰的函数调用,上下文对象是全局对象

// 例5 隐式绑定
function foo() {
    console.log(this.a);
}
function doFoo(fn) {
    // fn其实引用的是foo
    fn(); // <-- 调用位置!
}
var obj = {
    a: 2,
    foo: foo
};
var a = "oops, global"; // a是全局对象的属性
doFoo(obj.foo); // "oops, global",同理,this是在调用时确认,此时真正调用的位置this指向外部全局对象。

// 例6 隐式绑定
function foo() {
    console.log(this.a);
}
var obj = {
    a: 2,
    foo: foo
};
var a = "oops, global"; // a是全局对象的属性
setTimeout(obj.foo, 100); // "oops, global" 内置函数setTimeout相当于包了一层函数调用

// setTimeout的实现类似下面的伪代码
function setTimeout(fn, delay) {
    // 等待delay毫秒
    fn(); // <-- 调用位置!
}

// 例7 通过call改变this指向,硬绑定
function foo() {
    console.log(this.a);
}
var obj = {
    a:2
};
foo.call(obj); // 2

// 例8 通过call绑定this后,不能再改变this的指向(apply同理),称之为硬绑定
function foo() {
    console.log(this.a);
}
var obj = {
    a:2
};
var bar = function() {
    foo.call(obj);
};
bar(); // 2
setTimeout(bar, 100); // 2
// 硬绑定的bar不可能再修改它的this,实际上是foo在调用时被分配了上下文(this),因此不再向外找this,外层的bar被指定任何上下文不对其产生影响
bar.call(window); // 2

// 例9 硬绑定典型应用场景——包裹函数
function foo(something) {
    console.log(this.a, something);
    return this.a + something;
}
var obj = {
    a:2
};
var bar = function() {
    return foo.apply (obj, arguments);
};
var b = bar (3); // 2 3,对于任何入参,都将其与对象obj的属性进行协同操作,并按设定的规则返回值
console.log(b); // 5

// 例10 例9中的obj也可以抽象为参数
function foo(something) {
    console.log(this.a, something);
    return this.a + something;
}
// 简单的辅助绑定函数
function bind(fn, obj) {
    return function() {
      return fn.apply(obj, arguments);
    };
}
var obj = {
    a:2
};
var bar = bind(foo, obj);
var b = bar(3); // 2 3
console.log(b); // 5

// 例11 例10中的操作在JavaScript已经被封装为内置函数bind
function foo(something) {
    console.log(this.a, something);
    return this.a + something;
}
var obj = {
    a:2
};
var bar = foo.bind(obj); // 将foo函数中的this绑定为obj
var b = bar(3); // 2 3
console.log(b); // 5

// 例11 内置函数的上下文参数
function foo(el) {
    console.log(el, this.id);
}
var obj = {
    id: "awesome"
};
// 调用foo(..)时把this绑定到obj
[1, 2, 3].forEach(foo, obj);// forEach传入的第二个参数就是上下文
// 1 awesome 2 awesome 3 awesome

// 例12 new 绑定
function foo(a) {
    this.a = a;
}
var bar = new foo(2);
console.log(bar.a); // 2,使用new来调用foo(..)时,我们会构造一个新对象并把它绑定到foo(..)调用中的this上

四种绑定方式存在优先级:new绑定 > 显式绑定 > 隐式绑定 > 默认绑定

// 例13 显式绑定优先级高于隐式绑定
function foo() {
    console.log(this.a);
}
var obj1 = {
    a: 2,
    foo: foo
};
var obj2 = {
    a: 3,
    foo: foo
};
obj1.foo(); // 2
obj2.foo(); // 3
obj1.foo.call(obj2); // 3
obj2.foo.call(obj1); // 2

// 例14 new绑定优先级高于隐式绑定
function foo(something) {
    this.a = something;
}
var obj1 = {
    foo: foo
};
var obj2 = {};

obj1.foo(2);
console.log(obj1.a); // 2

obj1.foo.call(obj2, 3);
console.log(obj2.a); // 3

var bar = new obj1.foo(4);
console.log(obj1.a); // 2
console.log(bar.a); // 4

绑定例外场景

// 例15 call将this绑定到null或undefined时,会应用默认绑定
function foo() {
    console.log(this.a);
}
var a = 2;
foo.call(null); // 2

// 例16 将this绑定到null的应用——函数柯里化
function foo(a, b) {
    console.log("a:" + a + ", b:" + b);
}
// 把数组“展开”成参数
foo.apply(null, [2, 3]); // a:2, b:3
// 使用bind(..)进行柯里化
var bar = foo.bind(null, 2);
bar(3); // a:2, b:3

// 例17 使用null来忽略this绑定会产生难以想象的结果,更安全的做法
function foo(a, b) {
    console.log("a:" + a + ", b:" + b);
}
// 我们的DMZ空对象,“DMZ”(demilitarized zone,非军事区)对象——它就是一个空的非委托的对象
var ø = Object.create(null);
// 把数组展开成参数
foo.apply(ø, [2, 3]); // a:2, b:3
// 使用bind(..)进行柯里化
var bar = foo.bind(ø, 2);
bar(3); // a:2, b:3

// 例18 间接引用
function foo() {
    console.log(this.a);
}
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };

o.foo(); // 3
(p.foo = o.foo)(); // 2,赋值表达式p.foo = o.foo的返回值是目标函数的引用,因此调用位置是foo()而不是p.foo()或者o.foo()。

// 例19 软绑定,解决使用硬绑定之后就无法使用隐式绑定或者显式绑定来修改this的问题
if (! Function.prototype.softBind) {
    Function.prototype.softBind = function(obj) {
      var fn = this;
      // 捕获所有 curried 参数
      var curried = [].slice.call (arguments, 1);
      var bound = function() {
          return fn.apply(
              (! this || this === (window || global)) ?
                  obj : this,
              curried.concat.apply(curried, arguments)
          );
      };
      bound.prototype = Object.create(fn.prototype);
      return bound;
    };
}

function foo() {
  console.log("name: " + this.name);
}

var obj = { name: "obj" },
    obj2 = { name: "obj2" },
    obj3 = { name: "obj3" };

var fooOBJ = foo.softBind(obj);

fooOBJ(); // name: obj

obj2.foo = foo.softBind(obj);
obj2.foo(); // name: obj2 <---- 看!! !

fooOBJ.call(obj3); // name: obj3 <---- 看!

setTimeout(obj2.foo, 10);
// name: obj   <---- 应用了软绑定

箭头函数不使用this的四种标准规则,而是根据外层(函数或者全局)作用域来决定this。

// 例20 箭头函数的词法作用域
function foo() {
    // 返回一个箭头函数
    return (a) => {
      //this继承自foo()
      console.log(this.a);
    };
}
var obj1 = {
    a:2
};
var obj2 = {
    a:3
};
var bar = foo.call(obj1);
bar.call(obj2); // 2, 不是3!
// foo()内部创建的箭头函数会捕获调用时foo()的this。由于foo()的this绑定到obj1,bar(引用箭头函数)的this也会绑定到obj1,箭头函数的绑定无法被修改。

// 例21 对比例20
function foo() {
    // 返回一个函数
    return function(a){
      console.log(this.a);
    };
}
var obj1 = {
    a:2
};
var obj2 = {
    a:3
};
var bar = foo.call(obj1);
bar.call(obj2); // 3,上下文被改变
foo.call(obj1).call(obj2); // 3,

// 例22 回调函数中的this
function foo() {
    setTimeout(() => {
      // 这里的this在词法上继承自foo()
      console.log(this.a);
    },100);
}
var obj = {
    a:2
};
foo.call(obj); // 2

补充说明:例8中的硬绑定第二个及之后的call不能改变this指向,为什么例21后面的call能改变this指向?

function foo() {
    console.log(this.a);
    console.log(this); // obj
}
var obj = {
    a:2
};
var bar = function() {
    foo.call(obj); // foo在此处被调用,指定了上下文为obj,即this指向obj
    console.log(this); // window
};
bar(); // bar在全局上被调用,上下文为全局,即this指向window

// 再看例8,bar在全局上下文被调用,bar的this(上下文)就是window;foo的this是obj,foo有自己的this,不再向外找this
function foo() {
    console.log(this.a);
}
var obj = {
    a:2
};
var bar = function() {
    foo.call(obj);
};
bar(); // 2

// 变体,此时foo调用处没找到this,向外找,最后找到window,因此取的是window上a
function foo() {
    console.log(this.a);
}
var obj = {
    a:2
};
var bar = function() {
    foo();
};
a = 11;
bar(); // 11;

// 再看例21,bar在声明时调用了foo,给foo指定了obj1为其上下文,在调用bar时,又给bar指定了obj2为bar的上下文(this),因此bar的this就是obj2,而foo的上下文不再对其产生影响。在例20中箭头函数没有自己的this,只能向外找,最后找到foo中的this作为其上下文。
function foo() {
    // 返回一个函数
    return function(a){
      console.log(this.a);
    };
}
var obj1 = {
    a:2
};
var obj2 = {
    a:3
};
var bar = foo.call(obj1); 
bar.call(obj2); // 3,上下文被改变
foo.call(obj1).call(obj2); // 3

标签:function,绑定,bar,JavaScript,关键字,obj2,var,foo,知道
From: https://www.cnblogs.com/lhjc/p/17834344.html

相关文章

  • javascript promise all实现图片顺序加载
    不使用promise时是异步加载,图片加载的顺序不固定<!doctypehtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,user-scalable=no,initial-scale=1......
  • 理解与使用Javascript中的回调函数
     js里的解释:Acallbackisafunctionthatispassedasanargumenttoanotherfunctionandisexecutedafteritsparentfunctionhascompleted.    从字面上理解下来就是,回调就是一个函数的调用过程。假如函数a有一个参数,这个参数是个函数b,当函数a执行完......
  • 甘特图的这些新玩法,你都知道吗?
    前言甘特图是项目管理、生产排程、节点管理中非常常见的一个功能。那么,有没有一种方法能够帮助将甘特图引入到系统中,让数据的进度、排程数据的展示更加直观,让管理更加高效。本葡萄今天为大家带来一个新的插件——甘特图插件。该插件是结合SpreadJS本身强大电子表格能力提供的一种......
  • JavaScript知识点
    JavaScript中的虚值constfalsyValues=["",0,null,undefined,NaN,false];简单的来说虚值就是是在转换为布尔值时变为false的值。如何检查值是否虚值使用Boolean函数或者!!运算符。usestrict"usestrict”是ES5特性,它使我们的代码在函数或整个脚本中处于严格模式。严格模式帮助......
  • 一个常见的 JavaScript 解构陷阱
    在日常的JavaScript编码中,我们经常使用解构语法来提取对象中的属性。假设我们有一个名为fetchResult的对象,代表从接口返回的数据,其中包含一个字段名为data。constfetchResult={data:null};在提取data字段时,为了避免接口未返回该字段而导致的问题,我们常常会使用......
  • JavaScript知识点
    JavaScript提供的异步模式1、回调函数(callbacks)2、事件监听3、Promise对象call、apply的区别call和apply都是改变this指向的方法,区别在于call可以写多个参数,而apply只能写两个参数,第二个参数是一个数组,用于存放要传的参数。call和apply的好处用call和apply实现更好的继承和扩展,更......
  • 前端学习-JavaScript学习-JavaScript高级程序设计-第2章笔记
    在HTML中使用JavaScript元素<script>元素元素属性MDN<script>:脚本元素属性使用状态描述charset可选、少用字符集defer可选、少用表示脚本可以延迟至文档完全被解析后实行,部分浏览器不支持language废弃编写代码使用的语言src可选包含要执行代码的......
  • JavaScript实现Date( ) ——日期格式化的三种常用方法
    Date()介绍Date()基本使用Date()日期对象是构造函数,必须使用new来调用我们的日期对象。若Date()没有参数时返回当前时间若Date(timer)有参数时返回参数设置的时间参数写法:'2012-2-208:54:32'(字符串)返回值格式:SunMay28202323:36:28GMT+0800(中国标准时间)......
  • JavaScript slice 方法用法与实例解析
    在JavaScript中,slice()是一个常用的数组方法,用于从现有数组中提取一部分元素,然后返回一个新的数组。它是一个非常有用的工具,可以帮助你在不改变原始数组的情况下操作数组的子集。本文将介绍slice()的基本概念、使用方法、实践案例和互动练习,以帮助你更好地理解和掌握这一功能......
  • JavaScript - js生成 txt 文件
    JavaScript-js生成txt文件/***方法定义*/functiondownload(filename,text){varelement=document.createElement('a');element.style.display='none';varcontent=encodeURIComponent(text);element.setAttribute('href&#......