5 件可能会让 JavaScript 初学者/OO 开发人员感到惊讶的事情
在Twitter上关注我,很高兴接受您对主题或改进的建议/Chris
TLDR;这不是对 JavaScript 的批评,只是承认它与 OO 语言略有不同,你可以诅咒 JS,也可以利用由此产生的模式为自己谋利。
我喜欢这门语言,但它的工作方式与我习惯的其他语言不同。
无论您是 JavaScript 初学者还是编程新手,JS 中总有一些内容可能会让您感到惊讶。仅仅因为让您感到惊讶并不意味着它是错误的,它只是与众不同、古怪或完全合理,这取决于您之前的经验。接下来讨论的每一个主题几乎都值得写成一篇文章甚至一本书,但这里只列举了:
-1- 真的真的等于
如果你学过其他语言,比如 Java,你应该知道一个=
表示赋值,一个==
表示比较。在 JavaScript 中,===
和都可以==
用来比较相等性。该用哪一个?有什么区别?==
还是只比较值?考虑这个例子:
if('2' == 2) {} // true
true
当值相同但类型不同时返回。
现在看这个例子:
if('2' === 2) {} // false
if(2 === 2) {} // true
上面的===
检测到'2'
和2
具有不同的类型,因此计算结果为false
。通常建议使用这种方式进行比较。
-2- 有很多方法可以创建对象
在 Java 或 C# 中,你有一个类。你可以从该类实例化一个对象。这很合理。JavaScript 提供了更多选项。你可以通过以下方式创建对象:
- 使用类,您可以使用关键字
class
在类的上下文中定义字段、方法、getter/setter。以下是示例:
class Person {
constructor(n) {
this.name = n;
}
getName() { return this.name; }
}
- 对象字面量,你可以定义一个对象而不需要定义类。你只需要
{}
。它看起来就像这样:
const person = {
name: 'chris',
city: 'location',
getAll() {
return `${this.name} ${this.city}`;
}
}
- Object create,你可以使用该方法
Object.create()
创建一个对象。它需要一个原型对象作为基础。以下是示例:
const address = {
city: '',
country: ''
}
const adr = Object.create(address);
adr.city = 'London';
adr.country = 'UK'
console.log(adr.city); // London
console.log(adr.country); // UK
块语句,看起来没有范围
块语句、、if
等for
不会while
创建局部作用域。这意味着你在其中创建的任何内容都可以在语句外部访问,如下所示:
for (var i =0; i< 10; i++) {
console.log(i);
}
console.log(i);
最后console.log()
会打印10
。这可能会让你感到惊讶。
是啊,JS为什么设计成这样
i
还活着呢?
问问 Brendan Eich,这是一个功能:)
为了使 JS 的行为像您可能知道的其他语言一样,您需要使用 alet
或 a const
,如下所示:
for (let i = 0; i< 10; i++) {
console.log(i);
}
console.log(i);
运行这段代码,现在显示i is not defined
。为什么这样有效?因为let
允许你声明仅限于块语句作用域的变量。所以,这是由于使用了关键字let
over var
,而不是块语句被赋予了作用域。(感谢 Will 的评论)
呼
-3- 上下文,this
你可能听过“没人知道什么是全局上下文”这个笑话this
。从一个空文件开始this
,就是全局上下文。考虑以下代码:
global.name = "cross";
function someFunction() {
console.log(this.name);
}
someFunction();
上面我们将name
变量赋值给global
(在 Node.js 中我们这样称呼它,在前端它应该是window
)。 的值this
来自全局上下文。
让我们看下面一个不同的例子:
var object = {
name: 'chris',
getName() {
console.log(`${this.name}`);
}
}
object.getName();
这里的值this
是对象本身,它知道name
是什么,即值chris
。
改变环境
我们可以改变this
现状。JavaScript 中有一些辅助方法可以让我们做到这一点bind()
、call()
和apply()
。再次考虑这个例子,但object
添加了以下内容:
global.name = "cross";
var object = {
name: 'chris',
getName() {
console.log(`${this.name}`);
}
}
function someFunction() {
console.log(this.name);
}
someFunction();
我们可以this
从全局上下文转换为 的上下文object
。下面我们将展示上述任何一种方法如何使用这一原则:
someFunction.bind(object)();
someFunction.call(object)
someFunction.apply(object)
现在它将打印chris
,而不是cross
。
这三种方法的使用方式通常略有不同,但对于这个例子来说,它们是相当等效的。
混乱this
好的,那么我们什么时候会对 的值感到困惑this
呢?这种情况不止发生过一次,但一个常见的地方是当我们尝试使用构造函数创建一个对象时,如下所示:
function Person(n) {
this.name = n || 'chris';
function getName() {
return this.name;
}
return {
getName
};
}
const person = new Person();
console.log(person.getName()) // undefined
这是因为this
一旦使用内部函数,内部函数就会发生变化new
。有不同的解决方案可以解决这个问题:
解决方案 1 - this = that
解决这个问题的一种方法是让它记住outer 的值this
。将上面的例子重写如下:
function Person(n) {
this.name = n || 'chris';
var that = this;
function getName() {
return that.name;
}
return {
getName
};
}
const person = new Person();
console.log(person.getName()) // 'chris'
它通过引入记住值的that
变量来解决这个问题。但还有其他解决方案。this
解决方案 2 - 箭头函数
function Person() {
this.name = 'chris';
const getName = () => {
return this.name;
}
return {
getName
}
}
const person = new Person();
console.log(person.getName()) // 'chris'
上述代码替换了function
箭头函数的关键字=>
。
解决方案 3 - 使用闭包
第三种解决方案是使用所谓的closure
。这不需要使用new关键字,而是依赖于 JavaScript 几乎不需要使用 的事实this
。请考虑以下代码:
function Person() {
var name = 'chris';
const getName = () => {
return name;
}
return {
getName
}
}
const person = Person();
console.log(person.getName()) // 'chris'
以上内容this
已完全删除。我们也不再使用new
。在我看来,这是最像 JavaScript 的模式。
解决方案 4 - 将方法放在原型上
在这种方法中,我们使用一个类:
function Person() {
this.name = 'chris';
}
Person.prototype.getName = function() {
return this.name;
}
const person = new Person();
console.log(person.getName()) // 'chris'
这是一个很好的解决方案,原因不止一个。它解决了this
问题,同时确保该方法只创建一次,而不是每个实例创建一次。
解决方案 5 - 使用类
这与第四种解决方案非常接近:
class Person {
constructor() {
this.name = 'chris'
}
getName() {
return this.name;
}
}
const person = new Person();
console.log(person.getName()) // 'chris'
由于本文篇幅过长,我无法一一列举所有可能与this
您想象的不一致的情况。希望这些解决方案能帮助您了解问题发生的时间以及解决方法。
-4-const
有效,但可能不是你想象的那样
我们已经看到了关键字是const
如何创建局部作用域的。等等,还有更多 :) 这个词const
让你觉得它会一直有这个值,它是一个常量,不变等等。好吧……看下面的例子:
const PI = 3.14 // exactly :)
PI = 3;
上面给出了错误Assignment to a constant variable
。
所以我无法改变它,好
我们来看另一个例子:
const person = {
name: 'chris'
}
person.name = 'cross';
一切正常,没有问题:)
等等,什么?你说值不能改变?
我说过这样的话吗?我没这么说。我说的是这个词const
听起来像这样。这const
意味着存在一个只读引用,即引用不能被重新赋值。我从来没有说过它不能被改变。请看这个来澄清:
const person = {
name: "chris",
};
person = {
name: 'chris'
}
上面给出了一个错误Cannot assign to a constant variable
。。
嗯,好的,我可以让它不可变吗?
那么你可以Object.freeze()
像这样使用:
Object.freeze(person)
person.name = "cross";
console.log(person.name) // 'chris'
太好了,有办法让一切都不可变
嗯。
那又怎样?
它只会在第一层卡住。考虑以下代码:
const person = {
name: "chris",
address: {
town: 'London'
}
};
Object.freeze(person)
person.name = "cross";
person.address.town = 'Stockholm';
console.log(person.address.town) // Stockholm
但是但是..没有办法冻结它吗?
为此,你需要一个深度冻结算法。不过,问问自己,你需要这个吗?我的意思是,在大多数情况下,你的常量通常是原语。
...
const
公平地说,这在其他语言中也有点类似。我的意思是,在 C# 中,static readonly
如果你想要不可变且锁定的引用,那么在 Java 中,你需要final
……
是啊,是啊,无论如何
-5- 函数调用后还有生命
我们来看下面这段代码:
function aFunction() {
let name = 'chris';
console.log(name) // prints chris
}
console.log(name)
没什么特别的,它不知道name
最后console.log()
一个是什么,因为它在函数之外。我们稍微修改一下:
function aFunction() {
let name = "chris";
return {
getName() {
return name;
},
setName(value) {
name = value;
}
}
}
const anObject = aFunction();
console.log(anObject.getName());
anObject.setName("cross");
console.log(anObject.getName());
此时它打印出chris
calling getName()
,好吧,您可能认为它绑定到了某个值。然后您调用setName()
,最后再次调用getName()
,这次它打印出cross
。那么为什么这令人惊讶呢?想想一个函数通常是如何工作的,您调用它,其中的变量就不再存在了。现在再次查看上面的代码,您会注意到,name
在函数停止执行很久之后,变量似乎仍然存在。如果您将它与 Objective-C 之类的语言进行比较,这并不奇怪。您已经习惯了引用计数,如果代码的某些部分不再引用某些东西,那么它就会被垃圾回收。显然,您仍然通过anObject
变量引用它。
不过,如果你有面向对象编程的背景,你可能会习惯于对象持有状态,并且状态存在于对象本身。在这种情况下,状态name
存在于对象外部的词法环境中,这很迷幻吧?;)
最简单的理解方法是用私有变量创建对象。最近我也越来越多地用这种方法创建对象。不过用类也没什么不好,随便你怎么想 :)
概括
我很乐意听听你对其他可能让你惊喜/困惑或让你的生活更美好的事情的评论。因为我对 JavaScript 的很多方面都有同样的感受——我打字少得多。
文章来源:https://dev.to/itnext/5-things-that-might-surprise-a-javascript-beginner-oo-developer-1nje