用实例带你走进this执行上下文世界[js篇一]
Posted 一断点
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用实例带你走进this执行上下文世界[js篇一]相关的知识,希望对你有一定的参考价值。
前言:
通过本文你将学习到this的多种用法和使用场景、执行上下文(执行环境)的相关概念。
javascript 中this 指向多变、使用场景复杂,也是因其的强大灵活,在前端进阶中也是重要一环,如果能熟练驾驭就能写出更简洁、高质量的代码。
1.执行上下文(执行环境)
在说this之前不得不先说说执行上下文,可以理解为当前代码的执行环境。js中执行环境大概分为三种情况:
- 全局执行上下文(js运行首次会进入该环境) ---->eg: window对象
- 函数执行上下文(函数调用)
- eval
JS首次运行都会创建全局执行上下文,并秉承栈先进后出的概念,将全局执行上下文放入栈底,全局执行上下文在浏览器关闭后出栈。栈底永远是全局执行上下,栈顶则是当前正在执行的上下文。
全局执行上下文之后每当有函数调用,都会创建一个函数执行上下文并且入栈。由于js 是单线程的,所以栈顶的上下文正在执行,则其他上下文需要等待。
执行上下文分为创建阶段、执行阶段、销毁阶段。确定this指向主要在创建阶段
通过下图你就能明白了(备注:图来源于网络)
2. this
用一句话概述this执行,广泛的说法是谁调用它,this就指向谁,也就是说this 指向是在发生调用时才确定的指向,其实这说法很笼统,按照全面来讲应该是:在发生调用函数时会创建属于该函数自身的执行上下文,执行上下文创建阶段会确定this的执行。总结==》this指向是在调用函数时根据执行上下文所动态确定的。
关于this,下面笔者将会围绕下图进行分析讲解
先熟悉几条规律:
- 在函数体中非显式/隐式绑定下,严格模式下this绑定到undefined,否则绑定到window/global 全局对象。
- call、apply、bind 显式调用,绑定到指定对象。
- 构造函数new 调用,绑定到新创建的对象上。
- 由上下文对象调用,绑定到该对象上。
- 箭头函数this由外层上下文绑定的this决定执向。
下面将结合实例分析this: 笔者将反着来说,先讲复杂的
箭头函数和this
箭头函数中的this指向非常固化,因为箭头函数没有自己的this,所以箭头函数中不能用call、bind、apply来改变this的指向。
- 在对象方法内实例场景
const fun = {
fn:function () {
setTimeout(function(){
console.log(this)
})
}
}
console.log(fun.fn()) // window
如果需要将this指向foo时,用箭头函数即可解决
const fun = {
fn:function () {
setTimeout(() =>{
console.log(this)
})
}
}
console.log(fun.fn()) // {fn: ƒ}
2.箭头函数在函数内部,以非方法形式使用
function Person() {
setTimeout(function(){
console.log(this)
})
}
var p = new Person(); // Window
// 箭头函数形式:
function Person() {
setTimeout(()=>{
console.log(this)
})
}
var p = new Person(); // Person{ }
箭头函数的this指向了构造函数新生成的对象,而普通函数指向了全局window对象。普通函数在定义的时候并不知道自己的this要指向哪里,所以在被调用的时候普通函数里的this会指向调用它的那个对象,而箭头函数本身没有this,所以他会直接绑定到定义它时的作用域内的this,通俗讲就是会直接绑定到它父级的执行上下文里的this.
this优先级
this绑定有显示、隐式绑定一说,通常把callindapply ew 等情况称为显示绑定,而根据关系调用来确定this指向称为隐式绑定,那么显示绑定和隐式绑定谁的优先级更高呢?
cont obj1={
a:1,
fun:fun
}
const obj2={
a:2,
fun:fun
}
function fun() {
console.log(this.a)
}
obj1.foo.call(obj2) // 2
obj2.foo.call(obj1) // 1
// 也就是说callapplyind显示绑定优先级更高
再看
const obj={}
function fun(a) {
this.a=a;
}
let foo = fun.bind(obj)
foo(1)
console.log(obj.a) // 1
上面代码通过bind,将fun 函数中的this绑定为obj对象。执行foo(1)之后obj对象为{a:1}
当foo作为构造函数时,let fss=new foo(2)
, console.log(fss.a) // 2
看foo函数本身是通过bind方法构造函数,其内部已经将this 绑定为obj,它再作为构造函数,通过new调用时,返回的实例已经与obj 解绑,
总结: new绑定修改了bind绑定中的this,因此new绑定优先于bind显示绑定
function foo() {
return a => {
console.log(this.a)
};
}
const obj1 = {
a: 2
}
const obj2 = {
a: 3
}
const bar = foo.call(obj1)
console.log(bar.call(obj2)) // 2
这里有些同学会疑问为什么会输出2 不是3,那他肯定没有好好看上面章节,由于foo()的this 绑定到obj1,bar的this也会绑定到obj1,箭头函数的绑定无法被修改
如果将foo 全修改成箭头函数时
var a = ‘老番茄‘
const foo =() => {
a => {
console.log(this.a)
};
}
const obj1 = {
a: 2
}
const obj2 = {
a: 3
}
var bar = foo.call(obj1)
console.log(bar.call(obj2)) // ‘老番茄‘
//如果不懂请翻到上面查看箭头函数和this 章节
bind/call/apply
区别: 他们都是用来改变相关函数this指向,但是call/apply 是直接惊醒相关函数调用(其两者区别主要体现在参数上),bing不会执行相关函数,而是返回一个自动绑定新this指向的新函数。
- fun.apply(obj, arg1, arg2)
- fun.call(obj, [arg1,arg2])
- fun.bind(obj, ‘arg1‘, ‘arg2‘)()
const obj = {
name: ‘zhangsan‘,
showName: function() {
console.log(this.name)
}
}
const bar = {
name: ‘lisi‘
}
console.log(foo.showName.call(bar)) // ‘lisi‘
bing/call/apply 高级难度往往在结合构造函数和组合式实现继承,构造函数笔者下面会讲解
构造函数和this
new 调用构造函数,具体做了什么?
- 创建新对象
- 将构造函数的this指向这个新对象
- 为这个对象增加属性和方法
- 最终返回新对象
function Foo(){
this.name=‘laofanqie‘,
}
const bar = new Foo()
console.log(bar.name) // laofanqie
可以用一下代码概述下过程:
var foo = {}
obj.__proto__ = Foo.prototype
Foo.call(obj)
构造函数中出现了显示return,那么要注意两种情况:
1.
function Foo() {
this.name = ‘laofanqie‘;
const obj={};
return obj;
}
const bar = new Foo();
console.log(bar.name) // undefined 此时bar ={}
2.
function Foo() {
this.name = ‘laofanqie‘;
return 1;
}
const bar = new Foo();
console.log(bar.name) // ‘laofanqie‘
总结: 如果构造函数中显式返回一个值,且返回的是一个对象,那么this就指向这个返回的对象;如果返回的不是一个对象,那么this仍然指向实例.
全局环境下的this
全局环境下的this,比较简单,不详细说明,直接看实例吧。
function f1 () {
console.log(this)
}
function f2 () {
‘use strict‘
console.log(this)
}
f1() // window
f2() // undefined
const foo = {
bar: 10,
fn: function() {
console.log(this)
console.log(this.bar)
}
}
var fn1 = foo.fn
fn1()
这里 this 仍然指向的是 window。虽然 fn 函数在 foo 对象中作为方法被引用,但是在赋值给 fn1 之后,fn1 的执行仍然是在 window 的全局环境中。
const foo = {
bar: 10,
fn: function() {
console.log(this)
console.log(this.bar)
}
}
foo.fn() // {bar:10,fn: f} 10
总结:在执行函数时,如果函数中的 this 是被上一级的对象所调用,那么 this 指向的就是上一级的对象;否则指向全局环境
总结: 通过以上的描述,this的确错综复杂,想要完全掌握还是得结合实践,本文章也尽可能的进行讲解说明,希望读者能各有所获,祝大家身体健康,武汉加油!。
觉得不错的请给个三连哦??,接下我会更新大家感兴趣的话题,让我们一起进步。
以上是关于用实例带你走进this执行上下文世界[js篇一]的主要内容,如果未能解决你的问题,请参考以下文章