欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

object取值_this的原理、函数的不同调用方式this取值、以及不同环境下this的取值、函数四种调用方法...

发布时间:2025/3/15 编程问答 26 豆豆
生活随笔 收集整理的这篇文章主要介绍了 object取值_this的原理、函数的不同调用方式this取值、以及不同环境下this的取值、函数四种调用方法... 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

一、this的原理

1.1问题的由来

学懂 JavaScript 语言,一个标志就是理解下面两种写法,可能有不一样的结果

var obj = {foo: function () {} };var foo = obj.foo;// 写法一 obj.foo()// 写法二 foo()

上面代码中,虽然obj.foo和foo指向同一个函数,但是执行结果可能不一样。请看下面的例子。

var obj = {foo: function () { console.log(this.bar) },bar: 1 };var foo = obj.foo; var bar = 2;obj.foo() // 1 foo() // 2

这种差异的原因,就在于函数体内部使用了this关键字。很多教科书会告诉你,this指的是函数运行时所在的环境。对于obj.foo()来说,foo运行在obj环境,所以this指向obj;对于foo()来说,foo运行在全局环境,所以this指向全局环境。所以,两者的运行结果不一样。

函数的运行环境到底是怎么决定的?举例来说,为什么obj.foo()就是在obj环境执行,而一旦var foo = obj.foo,foo()就变成在全局环境执行?

理解了这一点,你就会彻底理解this的作用。

1.2内存的数据结构

JavaScript 语言之所以有this的设计,跟内存里面的数据结构有关系。

var obj = { foo: 5 };

上面的代码将一个对象赋值给变量obj。JavaScript 引擎会先在内存里面,生成一个对象{ foo: 5 },然后把这个对象的内存地址赋值给变量obj。

也就是说,变量obj是一个地址(reference)。后面如果要读取obj.foo,引擎先从obj拿到内存地址,然后再从该地址读出原始的对象,返回它的foo属性。

原始的对象以字典结构保存,每一个属性名都对应一个属性描述对象。举例来说,上面例子的foo属性,实际上是以下面的形式保存的。

{foo: {[[value]]: 5[[writable]]: true[[enumerable]]: true[[configurable]]: true} }

注意,foo属性的值保存在属性描述对象的value属性里面。

1.3函数

这样的结构是很清晰的,问题在于属性的值可能是一个函数。

var obj = { foo: function () {} };

这时,引擎会将函数单独保存在内存中,然后再将函数的地址赋值给foo属性的value属性。

{foo: {[[value]]: 函数的地址...} }

由于函数是一个单独的值,所以它可以在不同的环境(上下文)执行。

var f = function () {}; var obj = { f: f };// 单独执行 f()// obj 环境执行 obj.f()

1.4环境变量

JavaScript 允许在函数体内部,引用当前环境的其他变量。

var f = function () {console.log(x); };

上面代码中,函数体里面使用了变量x。该变量由运行环境提供。

现在问题就来了,由于函数可以在不同的运行环境执行,所以需要有一种机制,能够在函数体内部获得当前的运行环境(context)。所以,this就出现了,它的设计目的就是在函数体内部,指代函数当前的运行环境。

var f = function () {console.log(this.x); }

上面代码中,函数体里面的this.x就是指当前运行环境的x。

var f = function () {console.log(this.x); }var x = 1; var obj = {f: f,x: 2, };// 单独执行 f() // 1// obj 环境执行 obj.f() // 2

上面代码中,函数f在全局环境执行,this.x指向全局环境的x。

在obj环境执行,this.x指向obj.x。

obj.foo()是通过obj找到foo,所以就是在obj环境执行。一旦var foo = obj.foo,变量foo就直接指向函数本身,所以foo()就变成在全局环境执行。

二、函数不同调用方式中this取值

JavaScript 中的 this,它可以是全局对象、当前对象或者任意对象,这完全取决于函数的调用方式。JavaScript 中函数的调用有以下几种方式:

  • 作为对象方法调用
  • 作为函数直接调用
  • 作为构造函数调用
  • 使用 apply 或 call,bind 调用

我们将按照调用方式的不同,分别讨论 this 的含义。

2.1作为对象方法调用

在 JavaScript 中,函数也是对象,因此函数可以作为一个对象的属性,此时该函数被称为该对象的方法,在使用这种调用方式时,this 被自然绑定到该对象

var test = { a:0, b:0,get:function(){return this.a; } } console.log(test.get()) //0var prop=30 var O={prop:37} function independent(){return this.prop } O.f=independentconsole.log(O.f()) //37 作为对象方法调用console.log(independent()) //30 作为函数直接调用

由此可见this是动态作用域,this的指向关键是看函数怎么调用而不是怎么创建

2.2作为函数直接调用

函数也可以直接被调用,此时 this 绑定到全局对象(在严格模式下为undefined)。在浏览器中,window 就是该全局对象。

function fn1 (){return this } console.log(fn1()) //windowfunction fn2 (){"use strict";//see strict modereturn this } console.log(fn2 ()) //undefined

2.3作为构造函数调用

  • 当函数作为构造函数调用时,this代表new出来的对象,并且对象的原型会指向构造器的prototype(构造器是Foo、foo.constructor=Foo )
  • 如果没有使用new而是直接调用函数,this===window;
function Foo (name){this.name = name ;console.log(this) } var foo = new Foo('Emma'); //Foo {name: "Emma"} Foo() //window

2.4使用 apply 或 call,bind 调用

apply 和 call允许切换函数执行的上下文环境(context),即 this 绑定的对象。this取得的是apply() 、call()、 bind()调用时传入的第一个参数(bind是ES5提供的方法,所以只有IE9及以上才支持)

2.4.1 apply()、call()

function add(c,d){return this.a + this.b +c + d; }var O = {a:1,b:3}add.call(O,5,7); //1+3+5+7add.apply(O,[10,20]) //1+3+10+20function bar(){console.log(Object.prototype.toString.call(this)) }bar.call(7) // "[object Number]"

2.4.2 bind()改变this取值

function f(){return this.a }var g = f.bind({a:"test"}) console.log(g()); //testvar O = {a:37,f:f,g:g}; console.log(O.f(),O.g()) //37,test

bind与new(用new调用bind改变不了this取值)

function foo(){this.b=100;return this.a } var func = foo.bind({a:1}) func(); //1 new func(); //{b:100}

直接调用func(),this会指向bind中的参数,所以返回值为1,由于执行this.b=100,所以this对象会有个b属性,最终this为{a: 1, b: 100}。用new的话return如果不是对象会把this作为返回值,并且this会被初始化为一个默认的空对象,对象的原型是构造器的prototype,所以这里虽然用了bind的方法,但是this仍然会指向没有bind的情况。this指向空对象,空对象的原型指向foo.prototype,空对象的b属性会设置为100,整个对象会作用返回值(return this.a 被忽视)所以结果为{b:100}.

bind与currying(函数柯里化:把一个函数拆成多个单元)

function add(a,b,c){return a + b + c }var func = add.bind(undefined,100); func(1,2) //103 (100 + 1 +2)var func2 = func.bind(undefined,200); func2(10) //310 (100+ 200 + 10)

有时候不需要一次性把函数都调用完,而是调用一次把前几个参数传完了以后得到了一个函数再去调用,并且每次都传入自己所需要传入的值。

add.bind不需要改变this,那么就传入undefined(null也可),但是我们提供了额外参数100,那么bind方法就会把100赋值给a,再调用的时候1会给b,2就会给c,所以最后答案是103。再func.bind一次a上次绑定了100,b就会绑定为200,再调用func2传入值为10,10赋值为c,所以func2调用结果为310。

function getConfig(color,size,otherOptions){console.log(color,size,otherOptions) }var defaultConfig=getConfig.bind(null,"#cc0000","1024*768");defaultConfig("123") //#cc0000","1024*768,123 defaultConfig("456") //#cc0000","1024*768,456

三、不同环境this的取值

3.1全局作用域中this(浏览器)

全局作用域中的this一般指向全局对象,在浏览器中全局对象就是window

console.log(window.document===document) //true console.log(this===window) //true this.a=37 console.log(window.a) //37

3.2对象原型链上的this

var o={ f:function(){ this.a + this.b } } var p=Object.create(o) p.a=1 p.b=4 console.log(p.f()) //5

p是空对象并且它的原型会指向o(p.__proto__===o),调用原型上的方法(对象本身没有此方法会沿着原型链向上查找,原型链上的this也可以指向Object.create创建的对象,此this的指向与new创建实例对象的指向类似)this.a和this.b仍然能取到对象p上的a和b。即原型链上的this可以指向所在的对象(p对象)

注:p的构造器不为o,为Object(p.constructor===Object),这是用Object.create创建对象与new实例创建对象的不同,如果是new创建的实例对象构造器就是o。

3.3get/set方法与this

此时this的指向与原型链上的this的指向类似

function modules(){return Math.sqrt(this.re*this.re + this.im*this.im) }var o={re:1,im:-1,get phase(){return Math.atan2(this.im,this.re)} }Object.defineProperty(o,"modules",{get:modules,enumerable:true,configurable:true })console.log(o.phase,o.modules) //logs -0.781.4142

get、set方法中的this,一般情况下也是会指向get,set方法所在的对象(与一般对象属性作为函数对象类似)。

参考:

JavaScript 的 this 原理

https://blog.csdn.net/qq_40646986/article/details/82657909

总结

以上是生活随笔为你收集整理的object取值_this的原理、函数的不同调用方式this取值、以及不同环境下this的取值、函数四种调用方法...的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。