4月阅读周·你不知道的JavaScript | 深入JavaScript,坚固的基础是点滴构造起来的
背景
去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。
没有计划的阅读,收效甚微。
新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。
这个“玩法”虽然常见且板正,但是有效,已经坚持阅读三个月。
4月份的阅读计划有两本,《你不知道的JavaScrip》系列迎来收尾。
已读完书籍:《架构简洁之道》、《深入浅出的Node.js》、《你不知道的JavaScript(上卷)》、《你不知道的JavaScript(中卷)》。
当前阅读周书籍:《你不知道的JavaScript(下卷)》。
深入JavaScript
值与类型
JavaScript提供了一个typeof运算符,该运算符可以用来查看值的类型:
var a;
typeof a; // "undefined"
a = 'hello world';
typeof a; // "string"
a = 42;
typeof a; // "number"
a = true;
typeof a; // "boolean"
a = null;
typeof a; // "object"--诡异,这是bug
a = undefined;
typeof a; // "undefined"
a = { b: 'c' };
typeof a; // "object"
对象
对象类型是指一个组合值,你可以为其设置属性(命名的位置),每个属性可以持有属于自己的任意类型的值。
var obj = {
a: 'hello world',
b: 42,
c: true,
};
obj.a; // "hello world"
obj.b; // 42
obj.c; // true
obj['a']; // "hello world"
obj['b']; // 42
obj['c']; // true
可以通过点号(如obj.a所示)或者中括号(如obj["a"]所示)来访问属性。点号更简短易读,因而尽量使用这种方式。
数组
数组是一个持有(任意类型)值的对象,这些值不是通过命名属性/键值索引,而是通过数字索引位置。如下所示:
var arr = ['hello world', 42, true];
arr[0]; // "hello world"
arr[1]; // 42
arr[2]; // true
arr.length; // 3
typeof arr; // "object"
因为数组是特殊的对象(正如typeof所暗示的那样),所以它们也可以有属性,其中包括自动更新的length属性。
函数
在JavaScript程序中,另一个常用的对象子类型是函数:
function foo() {
return 42;
}
foo.bar = 'hello world';
typeof foo; // "function"
typeof foo(); // "number"
typeof foo.bar; // "string"
函数也同样是对象的一个子类型,因为typeof返回"function",这意味着function是一个主类型,因此,function可以拥有属性,但通常只在很少的情况下才会使用函数的对象属性(如foo.bar)。
变量
在JavaScript中,变量的名称(包括函数名称)必须是有效的标识符。
函数作用域
如果使用关键字var声明一个变量,那么这个变量就属于当前的函数作用域,如果声明是发生在任何函数外的顶层声明,那么这个变量则属于全局作用域。
1、提升
无论var出现在一个作用域中的哪个位置,这个声明都属于整个作用域,在其中到处都是可以访问的。这一行为被比喻地称为提升(hoisting), var声明概念上“移动”到了其所在作用域的最前面。
2、嵌套作用域
声明后的变量在这个作用域内是随处可以访问的,包括所有低层/内层的作用域。
如果试图在一个作用域中访问一个不可访问的变量,那么就会抛出ReferenceError。如果试图设定尚未声明的变量,那么就会导致在顶层全局作用域创建这个变量或者出现错误。
严格模式
ES5为这个语言新增了“严格模式”,严格限制了某些行为的规则。一般来说,这些限制可以将代码保持在一个更安全、更适当的规范集合之内。另外,遵循严格模式也更容易让引擎优化你的代码。‘
根据严格模式编译指示放置的位置,你可以选择使用单独的函数或者整个文件来遵循严格模式:
function foo() {
'use strict';
// 这个代码是严格模式
function bar() {
// 这个代码是严格模式
}
}
// 这个代码不是严格模式
作为值的函数
函数本身也可以作为值赋给变量或者向其他函数传入,又或者从其他函数传出。因此,应该将函数值视为一个表达式,与其他的值或者表达式类似。
var foo = function () {
// ..
};
var x = function bar() {
// ..
};
第一个赋给变量foo的函数表达式被称为是匿名的,因为这个函数表达式没有名称。
第二个函数表达式是已命名的(bar),即使它的引用赋值给了变量x。虽然匿名函数表达式的使用仍然极为广泛,但通常更需要已命名函数表达式。
this标识符
如果一个函数内部有一个this引用,那么这个this通常指向一个对象。但它指向的是哪个对象要根据这个函数是如何被调用来决定。
this并不指向这个函数本身,意识到这一点非常重要,因为这是最常见的误解。
function foo() {
console.log(this.bar);
}
var bar = 'global';
var obj1 = {
bar: 'obj1',
foo: foo,
};
var obj2 = {
bar: 'obj2',
};
// --------
foo(); // “全局的”
obj1.foo(); // "obj1"
foo.call(obj2); // "obj2"
new foo(); // undefined
关于如何设置this有4条规则,上述代码中的最后4行展示了这4条规则。
- 在非严格模式下,foo()最后会将this设置为全局对象。在严格模式下,这是未定义的行为,在访问bar属性时会出错——因此"global"是为this.bar创建的值。
- obj1.foo()将this设置为对象obj1。
- foo.call(obj2)将this设置为对象obj2。
- new foo()将this设置为一个全新的空对象。
原型
当引用对象的某个属性时,如果这个属性并不存在,那么JavaScript会自动使用对象的内部原型引用找到另外一个对象来寻找这个属性。
从一个对象到其后备对象的内部原型引用的链接是在创建对象时发生的。展示这一点的最简单的方法就是使用内置工具Object.create(..)。
更自然应用原型的方式是被称为“行为委托”的模式,其设计意图是,被链接对象能够将其所需要的行为委托给另外一个对象。
总结
学习使用JavaScript编程的第一步就是要了解其核心机制,比如值、类型、函数闭包、this以及原型。
作者介绍
非职业「传道授业解惑」的开发者叶一一。
《趣学前端》、《CSS畅想》等系列作者。华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。
如果看完文章有所收获,欢迎点赞👍 | 收藏⭐️ | 留言📝。
- 点赞
- 收藏
- 关注作者
评论(0)