热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

关于js的this,你了解多少?

问:什么是作用域?答:作用域就是代码中定义变量的区域问:作用域用来干啥?答:作用域规定了当前变量

问:什么是作用域?

答:作用域就是代码中定义变量的区域

问:作用域用来干啥?

答:作用域规定了当前变量的可访问范围以及权限

问:作用域有几种?

答:词法作用域,动态作用域,js中是词法作用域(也叫静态作用域)

问:这两种有什么区别?

答:词法作用域中函数的作用域在定义的时候就规定好了,而动态作用域的话需要运行的时候才能定义

问:作用域和上下文?

答:作用域和上下文不是一个概念,作用域从字面意思都能感觉到指的是一个区域,而上下文一般都是this,this就是指向当前函数的调用对象的一个引用,这叫上下文

so what is this(context)?

js中this是一直是一个比较神(che)奇(dan)的存在,但是如果你仔细去理解一遍的话,你会发现这些magic其实也不是什么神奇的事情,搞定它,你才能舒服的把玩js,this在js中的意义非常简单,永远指向函数的调用者,所以,要搞清楚this是什么 必须搞清楚当前函数的调用对象是哪个???

下面我们结合实例来看看神奇的this

eg1:

function foo(){console.log(this.a);
}var a = 2;
foo();???

在控制台尝试一下 发现打印出了2,所以this是什么?首先这里其实隐藏了2个小点,(1)默认在非严格模式下定义一个变量是会把这个变量定义在window上的,这里其实window.a就是2;(2)js中存在着变量的隐式提升,所有的变量会隐式的提升到当前作用域的顶端,所以其实执行的顺序是这样的

var a;
function foo(){console.log(this.a);
}a = 2; // 这个时候 window.a = 2了
foo(); // 输出window.a

总结1 默认情况下this是window,这是this的默认绑定规则

eg2:

var a = 3;
function foo(){console.log(this.a);
}
var obj = {a:2,foo: foo,
}obj.foo();

按照上面说的默认绑定规则这个地方应该输出3,实际运行发现输出的是2,这里介绍this的第二种绑定方式,隐式绑定,还记得一开始说过的,this永远指向调用该函数的对象,所以,这里调用foo的对象是obj,实际上foo并不属于obj,但是obj的foo属性指向了foo方法,因此obj调用foo的时候this被绑定到obj,所以最后打印this.a其实就是obj.a,当然输出是2了

eg3:

var a = 3;
function foo() {console.log(this.a);
}
var obj = {a: 2,foo: foo,
}var obj2 = {a: 4,obj: obj,
}obj2.obj.foo();

这里需要注意的是隐式绑定规则只对对象属性引用链中的最后一层起作用,也就是说当前输出的是2 并不是4,当前的this还是obj而不是obj2,如果想让他指向obj2我们必须采取一点手段,考虑下面这个函数

eg4:

var a = 3;
function foo() {console.log(this.a);
}
var obj = {a: 2,foo: foo,
}var foo1 = obj.foo;foo1();

注意这一步, var foo1 = obj.foo,这一步只是一个函数的别名,现在foo1指向的也是foo这个函数了,但是执行foo1的时候,当前调用foo1的在默认绑定的原则下是window(取决于是否是严格模式),所以当前输出的是3

总结2 隐式情况下,如果包含this的function被某一个对象调用了,那么this指向的就是当前调用对象,这是this的隐式绑定规则。但是要注意如果是函数别名,或者在执行默认绑定的时候改变了函数的调用位置,就要注意当前的this指向问题了~这就是this的隐式丢失问题

显示绑定,上面的例子中,当我们用obj去调用foo的时候,实际上对象和函数之间是通过原型来关联的,同时js也给我们提供了改变这种特性的一些方法,比较常用的有call和apply,任何一个你写的js函数都有这两个方法,它不会随宿主环境发生变化,同时这2个函数都可以显示的改变当前函数的调用对象,我们称之为显示的硬绑定

eg5:

function foo() {console.log(this.a);
}
var obj = {a: 2,foo: foo,
}var obj1 = {a: 3,foo: foo,
}obj.foo.call(obj1); // 3

这个地方隐式绑定会使得obj在调用foo的时候,this指向obj本身,但是call方法显示的改变了this的指向,让它显示的指向了obj1,因此输出了obj1.a,apply的功能和call是一样的,那么区别是什么,区别在于他们需要后续给函数传递参数的方式不同,call需要依次把参数带上,而apply需要把参数都放在一个数组中;

eg6:

function foo(b, c) {console.log(this.a, b, c);
}
var obj = {a: 2,foo: foo,
}var obj1 = {a: 3,foo: foo,
}obj.foo.call(obj1, 'b', 'c'); // 3,'b','c'
obj.foo.apply(obj1, ['b', 'c']) // 3,'b','c'

可以看出都改变了this的指向,同时都把参数带下来了

硬绑定的使用场景,创建一个包裹函数

eg7:

function foo(something) {console.log(this.a, something);return this.a + something;
}
var obj = {a: 2,
}var bar = function(){return foo.apply(obj, arguments);
}console.log(bar(3)); // 5

实际上,我们常用的办法是使用硬绑定实现一个辅助函数

function bind(fn, obj) {return function () {fn.apply(obj, arguments);}
}var obj = {a: 2
}function foo(){console.log(this.a);
}var bar = bind(foo, obj)(); // 2

由于这种bind方式的硬绑定是一个很常见的操作,因此es5提供了内置的bind函数,Function.prototype.bind,当然真实的bind比我们上面写的例子要复杂很多,不过思路是一样的,看下面的例子

eg8:

function foo(el) {console.log(el, this.id);
};var obj = {id: 2
};[1, 2, 3].forEach(foo);// 1 undefined, 2 undefined, 3 undefined
[1, 2, 3].forEach(foo, obj); // 1,2 2,2 3,2

内置的很多api都会允许我们传入一个上下文,作用和bind是一样的,目的是可以改变回调函数的上下文(是不是没用过?)

总结3:利用call和apply可以显示的改变函数的上下文,使其指向不同的对象,这种绑定方式叫做this的硬绑定。

最后介绍js中最后一种绑定this的方法,叫做 new绑定,在介绍之前,首先要区分清楚传统的面向对象语言的new 和js的new 长得虽然是一样的,但是实际的流程是完全不一样的,js中的对一个function进行new的时候流程是这样的:

1.创建(构造)一个全新的对象;

2.这个对象进行【prototype】连接;

3.绑定这个新对象到函数调用的this;

4.默认情况下,返回这个新的对象;

eg9:

function foo(a) {this.a = a;
}
var bar = new foo(2);
console.log(bar.a); // 2

总结4 new操作符是最后一种可以改变函数this的办法

-------------------------------分割线-----------------------------

接下来,看下四种this绑定方式的优先级问题

默认绑定是优先级最低的,因为它没有处理任何东西

显示绑定 vs 隐式绑定?

eg10:

function foo() {console.log(this.a);
};var obj = {a: 2,foo: foo
}var obj1 = {a: 3,foo: foo
}obj.foo(); // 2
obj.foo.call(obj1); // 3

明显使用call以后this发生了改变,因此显示的优先级比隐式的优先级要高

隐式绑定 vs new 绑定 eg11:

function foo(sth){this.a = sth;
}var obj = {foo: foo,
}obj.foo(2);var bar = new obj.foo(4);
console.log(obj.a); // 2
console.log(bar.a); // 4

所以new 绑定比隐式绑定的优先级要高

显式绑定 vs new 绑定

eg12:

function foo(sth) {this.a = sth;
}var obj = {foo: foo,
}var obj1 = {a: 3
}obj.foo.call(obj1);var bar = new obj.foo(4).call(obj1); // error 无法同时使用new 和 call
console.log(obj.a);
console.log(bar.a);

因为 call 和new 是无法同时使用的,我们换一个思路

eg13:

var obj = {}function foo(sth) {this.a = sth;
}var bar = foo.bind(obj);
bar(2);
console.log(obj.a); // 2
var bar1 = new bar(3)
console.log(bar1.a); // 3

此时,bar1 是通过new了一个bind过的函数得到的,但是new出来的bar1中的this明显发生了改变,因此可以得出的结论如下:

总结5 this绑定顺序:new > 显式 > 隐式 > 默认

你以为这就完了吗?

no 有规则就会有例外,比如: null

function foo() {console.log(this.a);
}var a = 3;var obj = {a: 2,foo: foo
}console.log(obj.foo.call(null)) // 3

总结6 this显示绑定如果绑定到null或者undefined上,在非严格模式下会使得this绑定到window上去

作用:

  • 展开数组

function foo(a, b) {console.log(`a:${a},b:${b}`);
}foo.apply(null, [2,3]); // a:2,b:3

  • 函数柯里化

function foo(a, b) {console.log(`a:${a},b:${b}`);return a + b;
}
var sum = foo.bind(null, 2);
console.log(sum(3)); // a:2,b:3 // 5

。。。

ES6出现了一个新的语法糖,箭头函数,要注意了,上面的所有规则均不适合箭头函数,因为箭头函数是没有this的,它的this完全是上层的this来决定,也就是说它的this是继承上层this来的,看例子

function foo() {return () => {console.log(this.a)}
}var obj1 = {a: 2
}var obj2 = {a: 3
}foo.call(obj1).call(obj2); // !!!输出是2不是3

你还记得夏明湖畔的self吗?

以前:

function foo() {var self = this;setTimeout(function () {console.log(self.a)}, 0);
}var obj = {a: 2
}foo.call(obj); // 2

现在:

function foo() {setTimeout(() => {console.log(this.a)}, 0);
}var obj = {a: 2
}foo.call(obj); // 2

------but----- 前方危险,兄弟,箭头函数 请不要乱用~~~

$('body').on(click, () => {console.log(this); // undefined or window
})$('body').on(click, function () {console.log(this); // body dom object
})

这里回调的时候,是动态绑定context的,强行使用箭头函数,会导致你在严格模式下什么都拿不到;

const foo = (a) => {this.a = a;
}
const bar = new foo(1); //error

构造函数要是没有自己的this?你敢想象吗?

另外,想想: react 为什么要bind this?因为你调用的时候是这个姿势



推荐阅读
  • 本文是一篇翻译文章,介绍了async/await的用法和特点。async关键字被放置在函数前面,意味着该函数总是返回一个promise。文章还提到了可以显式返回一个promise的方法。该特性使得async/await更易于理解和使用。本文还提到了一些可能的错误,并希望读者能够指正。 ... [详细]
  • React基础篇一 - JSX语法扩展与使用
    本文介绍了React基础篇一中的JSX语法扩展与使用。JSX是一种JavaScript的语法扩展,用于描述React中的用户界面。文章详细介绍了在JSX中使用表达式的方法,并给出了一个示例代码。最后,提到了JSX在编译后会被转化为普通的JavaScript对象。 ... [详细]
  • 本文讨论了将HashRouter改为Router后,页面全部变为空白页且没有报错的问题。作者提到了在实际部署中需要在服务端进行配置以避免刷新404的问题,并分享了route/index.js中hash模式的配置。文章还提到了在vueJs项目中遇到过类似的问题。 ... [详细]
  • Webpack5内置处理图片资源的配置方法
    本文介绍了在Webpack5中处理图片资源的配置方法。在Webpack4中,我们需要使用file-loader和url-loader来处理图片资源,但是在Webpack5中,这两个Loader的功能已经被内置到Webpack中,我们只需要简单配置即可实现图片资源的处理。本文还介绍了一些常用的配置方法,如匹配不同类型的图片文件、设置输出路径等。通过本文的学习,读者可以快速掌握Webpack5处理图片资源的方法。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • 本文介绍了如何使用Express App提供静态文件,同时提到了一些不需要使用的文件,如package.json和/.ssh/known_hosts,并解释了为什么app.get('*')无法捕获所有请求以及为什么app.use(express.static(__dirname))可能会提供不需要的文件。 ... [详细]
  • 如何提高PHP编程技能及推荐高级教程
    本文介绍了如何提高PHP编程技能的方法,推荐了一些高级教程。学习任何一种编程语言都需要长期的坚持和不懈的努力,本文提醒读者要有足够的耐心和时间投入。通过实践操作学习,可以更好地理解和掌握PHP语言的特异性,特别是单引号和双引号的用法。同时,本文也指出了只走马观花看整体而不深入学习的学习方式无法真正掌握这门语言,建议读者要从整体来考虑局部,培养大局观。最后,本文提醒读者完成一个像模像样的网站需要付出更多的努力和实践。 ... [详细]
  • 在JavaScript中,函数没有重载的概念,如果声明了多个重名的函数,不管函数的形参个数是否一样,只有最后一个有效。如果调用函数时传入的参数个数与函数定义时的参数个数不符,会出现不同的情况。函数调用时,传入的参数个数少于函数定义时的参数个数,未传入的参数会被当做undefined处理,可能会导致错误。而传入的参数个数多于函数定义时的参数个数,多余的参数不会被使用,但不会报错。 ... [详细]
  • 本文介绍了一种在PHP中对二维数组根据某个字段进行排序的方法,以年龄字段为例,按照倒序的方式进行排序,并给出了具体的代码实现。 ... [详细]
  • PHP引用的概念和用法详解
    本文详细介绍了PHP中引用的概念和用法。引用是指不同的变量名访问同一个变量内容,类似于Unix文件系统中的hardlink。文章从引用的定义、作用、语法和注意事项等方面进行了解释和示例。同时还介绍了对未定义变量使用引用的情况,以及在函数和new运算符中使用引用的注意事项。 ... [详细]
  • 简述在某个项目中需要分析PHP代码,分离出对应的函数调用(以及源代码对应的位置)。虽然这使用正则也可以实现,但无论从效率还是代码复杂度方面考虑ÿ ... [详细]
  • Vue基础一、什么是Vue1.1概念Vue(读音vjuː,类似于view)是一套用于构建用户界面的渐进式JavaScript框架,与其它大型框架不 ... [详细]
  • 详解react组件通讯方式(多种)
    这篇文章主要介绍了详解react组件通讯方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着 ... [详细]
  • python+selenium十:基于原生selenium的二次封装fromseleniumimportwebdriverfromselenium.webdriv ... [详细]
  • React 小白初入门
    推荐学习:React官方文档:https:react.docschina.orgReact菜鸟教程:https:www.runoob.c ... [详细]
author-avatar
山海
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有