【原理】理解JavaScript中的上下文-对象字面量

简介: 【原理】理解JavaScript中的上下文-对象字面量

在JavaScript中,“context”指的是一个对象。在一个对象中,关键字“this”指向该对象,并提供了一个指向作为该对象成员的属性和方法的接口。当函数被执行时,关键字“this”指向函数被执行的对象。

通常有这些场景来说明 this 的指向:

  • 当函数在全局上下文中执行时,“this”指的是全局或“window”对象
  • 当一个函数是一个对象的方法时,“this”指的是那个对象(除非它是在另一个对象的上下文中手动执行的)
  • 当一个函数在另一个函数内部执行时(无论嵌套有多深)“this”指的是执行该函数的上下文所在的对象
  • 实例对象内部实例化构造函数时“this”指的是实例对象

所以,如你所见,“this”很容易让你头疼。但是坚持住💪。

简而言之,在对象字面量中,你没有局部变量,你有对象的属性。在函数foo()中,我可以说" var drink = ' beer ';,对于一个叫做bar的对象,我会说bar.dink=“beer”。不同的是,“beer”“bar”的属性,而当函数执行时,局部变量是由“var”关键字定义的,不能被函数之外的任何人或任何东西看到。

为什么“this”在JavaScript中如此重要?

示例 1

drink = 'wine';
var foo = {
    drink: "beer",
    getDrink: function(){
        return drink;
    }    
};
console.log( foo.getDrink() ); // wine

在示例# 1中,我们首先创建一个名为“drink”的全局变量,并将其值赋值为“wine”

接下来,我们创建一个名为“foo”的对象字面量,其属性“drink”等于“beer”。还有一个方法getDrink简单地返回了“drink”但是为什么它返回“wine”而不是“beer”呢?这是因为在对象“foo”中,“drink”foo的属性,而不是变量

在函数内部,当引用一个变量时,JavaScript引擎搜索作用域链并返回它找到的第一个匹配项。

虽然这个函数在“foo”上下文中执行,但“foo”没有名为“drink”的变量。它有一个名为“drink”的属性,但没有变量。因此JavaScript引擎搜索作用域链的下一级。作用域链的下一层是全局对象,它包含一个名为“drink”的变量,因此返回该变量的值“wine”

示例 2

var drink = 'wine';
var foo = {
    drink: "beer",
    getDrink: function(){
        return this.drink; // 'this' refers to the object "foo"
    }    
};
console.log( foo.getDrink() ); // beer

在例2中,我们所做的唯一改变是,在分配给“getDrink”的匿名函数中,我们返回“this.drink”,而不是“drink”

这是一个重要的细节。当函数在对象的上下文中执行时,关键字“this”指向该对象。你可以通过使用“this”关键字来访问对象的任何属性,添加新的属性(例如this.Color = " blue "),并更改现有的(例如:this.drink=“juice")。

使用点表示法创建一个JavaScript对象字面量

示例 3

var drink = 'wine';
var foo = {};
foo.drink = "beer";
foo.getDrink = function(){
        return this.drink; // 'this' refers to the object "foo"
    };
console.log( foo.getDrink() ); // beer

在例3中,我们拥有与例2完全相同的功能。从JavaScript引擎的角度来看,我们实现了相同的目标,控制台输出也完全相同。

区别在于我们如何组织代码。在例2中,我们在创建对象字面量“foo”的同时创建了属性“drink”“getDrink”。这都是一种表达式。在例# 3中,我们首先创建了一个名为“foo”的空对象,然后使用点表示法逐个向对象添加属性。我只是想指出,从语法的角度来看,解决所有这些问题的方法不止一种。

对象字面量可以包含其他对象字面量,而那些对象有它们自己的上下文

示例 4

var drink = 'wine';
var foo = {};
foo.drink = "beer";
foo.getDrink = function(){
        return this.drink; // 'this' refers to the object "foo"
    };
foo.under21 = {};
foo.under21.drink = 'soda';
foo.under21.getDrink = function(){
        return this.drink; // 'this' refers to the object "foo.under21"
    };
console.log( foo.getDrink() ); // beer
console.log( foo.under21.getDrink() ); // soda

在例 4中,我们为“foo”添加了一个新属性,该属性也是另一个对象。一开始它是空的(例如foo.under21 ={}),然后添加两个属性。第一个属性是“drink”,它被赋值为“soda”。不要把它和属性“drink”混淆,它是在“foo”上下文中设置的,等于“beer”。在“foo.under21”的上下文中,“drink”等于“soda”

foo的第二个属性“foo.under21”有一个匿名的功能。这个匿名函数返回“this.drink”。在“foo.Under21"上下文中,"drink"等于"soda ",因此调用该函数返回"soda"

这个例子的重点是对象字面量的属性本身可以是对象,并且那些对象有它们自己的上下文。当函数在这些对象的上下文中执行时,“this”指的是对象。我知道这种对象嵌套是没有限制的

JavaScript的.call()和.apply()方法允许你动态地改变函数执行的上下文

示例 5

drink = 'wine';
var foo = {};
foo.drink = "beer";
foo.getDrink = function(){      
        return this.drink; // 'this' refers to the object "foo"
    };
foo.under21 = {};
foo.under21.drink = 'soda';
foo.under21.getDrink = function(){
       return foo.getDrink.call(); // execute foo.getDrink()                
    };
console.log( foo.getDrink() ); // beer
console.log( foo.under21.getDrink() ); // wine

好的,这里是额外的问题:在示例# 5中,为什么调用" foo.under21.getDrink() "返回的是" wine " ?

这是因为我们改变了该函数的内部工作方式。而不是简单地返回“this.drink。我们使用JavaScript的" .call() "方法,它允许你在另一个对象的上下文中执行任何函数。当你没有指定函数被“调用”的上下文时,它会在全局对象的上下文中执行。在全局上下文中,有一个名为“drink”的变量,它等于“wine”,因此返回“wine”

示例 6

drink = 'wine';
var foo = {};
foo.drink = "beer";
foo.getDrink = function(){      
        return this.drink; // 'this' refers to the object "foo"
    };
foo.under21 = {};
foo.under21.drink = 'soda';
foo.under21.getDrink = function(){
       return foo.getDrink.call(this); // execute foo.getDrink()                
    };
console.log( foo.getDrink() ); // beer
console.log( foo.under21.getDrink() ); // soda

在示例# 6中,返回" soda "是因为当我们使用JavaScript的" .call() "方法时,我们指定了函数将要在其中执行的上下文。在本例中,我们指定的上下文是“this”。“this”指的是“foo.under21”的上下文。“foo.Under21”有一个名为“drink”的属性,因此返回值“soda”

总结

我想指出的是,当你开始理解JavaScript对象字面量中的上下文概念时,必须意识到还有更多的内容需要考虑。JavaScript对象字面量可以有属性是对象,这些对象有它们自己的上下文。

在每种情况下,当函数在该上下文中执行时,在函数内部,“this”关键字指向函数的属性对象,因为函数是在该对象的上下文中执行的。通过使用JavaScript的" .call() "方法(或" .apply() "方法),可以通过这种方式改变函数执行的上下文,从而相应地改变" this "的指向。


相关文章
|
7天前
|
机器学习/深度学习 JavaScript 前端开发
JS进阶教程:递归函数原理与篇例解析
通过对这些代码示例的学习,我们已经了解了递归的原理以及递归在JS中的应用方法。递归虽然有着理论升华,但弄清它的核心思想并不难。举个随手可见的例子,火影鸣人做的影分身,你看到的都是同一个鸣人,但他们的行为却能在全局产生影响,这不就是递归吗?雾里看花,透过其间你或许已经深入了递归的魅力之中。
52 19
|
7月前
|
JavaScript 前端开发
如何在 JavaScript 中使用 __proto__ 实现对象的继承?
使用`__proto__`实现对象继承时需要注意原型链的完整性和属性方法的正确继承,避免出现意外的行为和错误。同时,在现代JavaScript中,也可以使用`class`和`extends`关键字来实现更简洁和直观的继承语法,但理解基于`__proto__`的继承方式对于深入理解JavaScript的面向对象编程和原型链机制仍然具有重要意义。
|
2月前
|
编解码 JavaScript 前端开发
【Java进阶】详解JavaScript的BOM(浏览器对象模型)
总的来说,BOM提供了一种方式来与浏览器进行交互。通过BOM,你可以操作窗口、获取URL、操作历史、访问HTML文档、获取浏览器信息和屏幕信息等。虽然BOM并没有正式的标准,但大多数现代浏览器都实现了相似的功能,因此,你可以放心地在你的JavaScript代码中使用BOM。
88 23
|
7月前
|
Web App开发 JavaScript 前端开发
如何确保 Math 对象的方法在不同的 JavaScript 环境中具有一致的精度?
【10月更文挑战第29天】通过遵循标准和最佳实践、采用固定精度计算、进行全面的测试与验证、避免隐式类型转换以及持续关注和更新等方法,可以在很大程度上确保Math对象的方法在不同的JavaScript环境中具有一致的精度,从而提高代码的可靠性和可移植性。
|
8月前
|
自然语言处理 JavaScript 前端开发
深入理解JavaScript中的闭包:原理与实战
【10月更文挑战第12天】深入理解JavaScript中的闭包:原理与实战
|
3月前
|
JavaScript 前端开发 Java
深入理解 JavaScript 中的 Array.find() 方法:原理、性能优势与实用案例详解
Array.find() 是 JavaScript 数组方法中一个非常实用和强大的工具。它不仅提供了简洁的查找操作,还具有性能上的独特优势:返回的引用能够直接影响原数组的数据内容,使得数据更新更加高效。通过各种场景的展示,我们可以看到 Array.find() 在更新、条件查找和嵌套结构查找等场景中的广泛应用。 在实际开发中,掌握 Array.find() 的特性和使用技巧,可以让代码更加简洁高效,特别是在需要直接修改原数据内容的情形。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一
|
3月前
|
监控 JavaScript 前端开发
MutationObserver详解+案例——深入理解 JavaScript 中的 MutationObserver:原理与实战案例
MutationObserver 是一个非常强大的 API,提供了一种高效、灵活的方式来监听和响应 DOM 变化。它解决了传统 DOM 事件监听器的诸多局限性,通过异步、批量的方式处理 DOM 变化,大大提高了性能和效率。在实际开发中,合理使用 MutationObserver 可以帮助我们更好地控制 DOM 操作,提高代码的健壮性和可维护性。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
MutationObserver详解+案例——深入理解 JavaScript 中的 MutationObserver:原理与实战案例
|
3月前
|
JavaScript 前端开发 Java
详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用
柯里化是一种强大的函数式编程技术,它通过将函数分解为单参数形式,实现了灵活性与可复用性的统一。无论是参数复用、延迟执行,还是函数组合,柯里化都为现代编程提供了极大的便利。 从 Redux 的选择器优化到复杂的数据流处理,再到深度嵌套的函数优化,柯里化在实际开发中展现出了非凡的价值。如果你希望编写更简洁、更优雅的代码,柯里化无疑是一个值得深入学习和实践的工具。从简单的实现到复杂的应用,希望这篇博客能为你揭开柯里化的奥秘,助力你的开发之旅! 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一
|
7月前
|
JSON 前端开发 JavaScript
JavaScript中对象的数据拷贝
本文介绍了JavaScript中对象数据拷贝的问题及解决方案。作者首先解释了对象赋值时地址共享导致的值同步变化现象,随后提供了五种解决方法:手动复制、`Object.assign`、扩展运算符、`JSON.stringify`与`JSON.parse`组合以及自定义深拷贝函数。每种方法都有其适用场景和局限性,文章最后鼓励读者关注作者以获取更多前端知识分享。
75 1
JavaScript中对象的数据拷贝
|
7月前
|
JavaScript 前端开发 API
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
201 17