资讯专栏INFORMATION COLUMN

JavaScript中的this

Salamander / 996人阅读

摘要:指向的改变构造函数中的操作符会调用函数的内部的方法,创建对象,之后调用函数的方法,把新创建对象作为值。调用函数时与设置的值以及箭头函数皆为动态的改变指针的方法。这一特性使得箭头函数在中的函数中使用起来很方便。

原文地址

JavaScript中的this 原理 错误的this指向

通常所说的:如果是全局环境中,this指向全局对象,如果是对象的方法,这this指向这个对象。

例子1:

var foo = {
  bar: function() {
    console.log(this)
  }
}

foo.bar();
(foo.bar)();

(foo.bar = foo.bar)();
(false || foo.bar)();
(foo.bar, foo.bar)();

例子1前两者为foo,后面都是全局对象。后三者并没有指向foo。所以我们上面的通常说法不精确。

精确的this指向

在全局环境中,this指向全局对象。而在普通函数调用中,this是由激活上下文的调用者提供,即调用这个函数的父作用域,以及函数调用的语法形式,决定了this的值,这是一个动态可变的值。

例子2:

var foo = {
  bar: function() {
    console.log(this)
    console.log(this === foo)
  }
}

foo.bar() // foo, true

var fn = foo.bar

console.log(fn === foo.bar) // true

fn() // global, false

例子2中,第一次调用指向foo,把foo.bar赋值给fn之后,this没有指向foo。是什么导致this指向的变化呢?

this指向的内部原理

this是执行上下文的一个属性:

activeExecutionContext = {
  VO: {...},
  this: thisValue
}

在普通函数调用中,this是由激活上下文的调用者提供,即调用这个函数的父作用域,函数调用的语法形式,决定了this的值,这是一个动态可变的值。

为什么会引起这个差异呢?
因为引用类型的不同处理,是否会获取真实的值,所导致的。

引用类型存在形式:

1 标识符(变量名,函数名,函数参数名,全局对象属性名)

2 属性访问器(foo.bar(); foo["bar"](), 点标记法;可以动态设置属性名的方括号[]

为了从引用类型中获取真实的值,存在类似getValue的方法。而函数上下文的规则是,函数上下文中this由调用者提供,并由调用形式决定。如果调用的圆括号左侧是一个引用类型,this为这个引用类型,如果是非引用类型,这为null,但为null无意义,被隐式转化为全局对象。

为什么有this的特性?

this是一个指针,便于代码的更为简洁地复用。

// 无this
function upper(context) {
  return context.name.toUpperCase()
}
function speak(context) {
  var greeting = "Hello, I"m " + upper(context)
  console.log(greeting)
}

var me = {
  name: "m"
}

var you = {
  name: "y"
}

speak(me)

// 利用this

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

speak.call(me)

这里this可以简化上下文对象的传递。其他OPP语言中this关键字和OPP密切相关,一般是引用刚创建的对象,但在ECMAScript中,this只限于引用创建过的对象,this的指向和函数调用形式有关,不一定引用类型调用就指向引用类型。

this指向的改变

1 构造函数中的this

function C() {
  console.log(this)
  this.x = 10
}

var a = new C()
console.log(a.x);

new操作符会调用函数的内部的Construct方法,创建对象,之后调用函数的Call方法,把新创建对象作为this值。

2 调用函数时call与apply设置this的值

var b = 10
function a(c) {
  console.log(this.b)
  console.log(c)
}

a(20)
a.call({b: 20}, 30)
a.apply({b: 20}, [40])
call, apply, bind以及箭头函数

call,apply,bind皆为动态的改变this指针的方法。其中call和apply是当Object没有某个方法,但是其它对象有,可以借助call和apply改变this的指向,调用其它对象的方法。bind为绑定this为某个对象。

典型的应用:

将类数组元素转化为数组:
Array.prototype.slice.apply(document.getElementsByTagName("*"))

检查类型:

function isArray(obj) {
  return Object.prototpye.toString.call(obj) === "[object Array]"
}

箭头函数则与前三者不同。
If kind is Arrow, set the [[ThisModel]] internal slot of F to lexical.If the
value is "lexical", this is an ArrowFunction and does not have a local this

If thisModel is lexical, return NormalCompletion(undefined).

箭头函数没有自己的this绑定,同时在函数执行时绑定this会被直接忽略。其中this总是指向定义时所在的对象,而不是运行时所在的对象。即箭头函数的this值是lexical
scope 的this值。这一特性使得箭头函数在React中的render函数中使用起来很方便。

function foo() {
  setTimeout(() => {
    console.log("id: ", this.id)
  }, 100)
}

var id = 0

foo.call({id: 42})

// 容易误解的地方
// {id: 42}
// 是箭头函数定义所在的对象还是运行时所在的对象。由于箭头函数位于foo函数内部,只有foo函数运行之后他才会生成,所以foo运行时所在的对象,即箭头函数定义所在的对象。
var f = () => 5;
// 近似等价于
var f = function() {return 5;}.bind(this);

综上,call,apply,bind使得JavaScript具有动态改变this的特性,而箭头函数使得JavaScript具有固定this的指向的特性。一动一静,相得益彰。

在编程中的运用 ES7中的::
  this.x = 0
  let module = {
    x: 1,
    getX: function() {
      console.log(this.x)
    }
  }
  module.getX()
  let get = module.getX
  get() // 0
  let boundGetX = get.bind(module)
  boundGetX() // 1
  let ES7boundGetx = module::get
  ES7boundGetx() // 1
super
class P {
  foo() {
    console.log("P.foo")
  }
}

class C extends P {
  foo() {
    super.foo()
  }
}

var c1 = new C()
c1.foo() // P.foo

var D = {
  foo: function() {
    console.log("D.foo")
  }
}

var E = {
  foo: C.prototype.foo
}

Object.setPrototypeOf(E, D)
E.foo() // P.foo

可见super的绑定是静态绑定,创建时即完成绑定。所以E委托了D,但并不能调用到D.foo(),类似于箭头的函数的this绑定。

jQuery中的this

链式调用的实现;

function Constructor() {
  this.art = 0
}

Constructor.prototype.fn_0 = function() {
  console.log("0")
  return this;
}

Constructor.prototype.fn_1 = function() {
  console.log("1")
  return this;
}

new Constructor().fn_0().fn_1()

调用的方法返回this即可。

end()的实现

function end() {
  return this.prevObject || this.constructor(null)
}

// 设置preObject的函数
function pushStack( ele ) {
  // Build a new jQuery macthed element set
  var ret = jQuery.merge( this.constructor(), elems);
  ret.prevObject = this // ret.pervObject 设置为当前jQuery对象引用
  ret.context = this.context
  return ret;
}

pushStack函数在很多涉及DOM操作的函数都有调用,用于缓存了当前的this。由于只存储当前,所以这里只需要一个preObject即可,无需放在一个数组里。

利与弊

this是JavaScript特性之一,具有脚本语言的动态特性,带来很多便捷,同时由于super和箭头函数的特性,使得this具有了静态的特性,在这两种情况下,this是固定且无法改变的。其利与弊都是this的灵活,双刃剑。所以才有了ES2015中super和箭头函数的固定this的特性。

拾遗

this可被重新赋值么?(不能,this是保留字)

问题(答案见原文)

1 call参数为null时,this的指向

function a() {
  console.log(this)
}
a.call(null)

2 调用形式对this的影响

var foo = {
  bar: function() {
    console.log(this)
  }
}

foo.bar();
(foo.bar)();

(foo.bar = foo.bar)();
(false || foo.bar)();
(foo.bar, foo.bar)();
参考资料:

《你所不知道的JavaScript(上卷)》

关于JavaScript的执行域,标识符解析,闭包的研究

深入ECMA-262-3 第三章、This

JavaScript内部原理实践——真的懂JavaScript吗?

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/81479.html

相关文章

  • JavaScript深入浅出

    摘要:理解的函数基础要搞好深入浅出原型使用原型模型,虽然这经常被当作缺点提及,但是只要善于运用,其实基于原型的继承模型比传统的类继承还要强大。中文指南基本操作指南二继续熟悉的几对方法,包括,,。商业转载请联系作者获得授权,非商业转载请注明出处。 怎样使用 this 因为本人属于伪前端,因此文中只看懂了 8 成左右,希望能够给大家带来帮助....(据说是阿里的前端妹子写的) this 的值到底...

    blair 评论0 收藏0
  • 学习React之前你需要知道的的JavaScript基础知识

    摘要:和类在开始时遇到类组件,只是需要有关类的基础。毕竟,中的条件呈现仅再次显示大多数是而不是特定的任何内容。 在我的研讨会期间,更多的材料是关于JavaScript而不是React。其中大部分归结为JavaScript ES6以及功能和语法,但也包括三元运算符,语言中的简写版本,此对象,JavaScript内置函数(map,reduce,filter)或更常识性的概念,如:可组合性,可重用...

    bitkylin 评论0 收藏0
  • JavaScript中的面向对象(object-oriented)编程

    摘要:对象在中,除了数字字符串布尔值这几个简单类型外,其他的都是对象。那么在函数对象中,这两个属性的有什么区别呢表示该函数对象的原型表示使用来执行该函数时这种函数一般成为构造函数,后面会讲解,新创建的对象的原型。这时的函数通常称为构造函数。。 本文原发于我的个人博客,经多次修改后发到sf上。本文仍在不断修改中,最新版请访问个人博客。 最近工作一直在用nodejs做开发,有了nodejs,...

    JerryZou 评论0 收藏0
  • JavaScript进阶之’this

    摘要:所以相同点是,在全局范围内,全局变量终究是属于老大的。只生效一次引入了。只生效一次在箭头函数中,与封闭词法环境的保持一致。我通常把这些原始函数叫做构造函数。在里面你可以嵌套函数,也就是你可以在函数里面定义函数。 showImg(https://img-blog.csdnimg.cn/20190522000008399.jpg?x-oss-process=image/watermark,...

    shenhualong 评论0 收藏0
  • 理解 JavaScript 中的 this 关键字

    摘要:原文许多人被中的关键字给困扰住了,我想混乱的根源来自人们理所当然地认为中的应该像中的或中的一样工作。尽管有点难理解,但它的原理并不神秘。在浏览器中,全局对象是对象。运算符创建一个新对象并且设置函数中的指向调用函数的新对象。 原文:Understanding the this keyword in JavaScript 许多人被JavaScript中的this关键字给困扰住了,我想混乱的...

    jayzou 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<