ravencrown / god-of-js Goto Github PK
View Code? Open in Web Editor NEWJavaScript 封神之路
JavaScript 封神之路
编译器在编译过程的第二步生成了代码,引擎执行它的时候,会通过查找变量 a
来判断它是否已声明过。查找的过程由作用域协助。
引擎的查找分为 LHS
和RHS
,L
和R
分别代表左侧和右侧,换句话说,当变量出现在赋值操作的左侧的时候进行LHS
,出现在右侧的时候进行RHS
更准确点,RHS
查询与简单地查找某一个变量的值一样,而LHS
查询则是试图找到变量的容器本身,从而可以对它赋值。
console.log(a)
这里对 a
的引用是一个 RHS
,因为这里 a
并没有赋值操作,只需要找到 a
的值
a = 2
则是一个 LHS
的引用,这里只想 为= 2
这个赋值操作找到一个目标
function foo( a ) {
console.log( a )
}
foo( 2 )
这里面的 RHS
有
这里面有几个 LHS(3处),有几个RHS(四处)
function foo( a ){
var b = a;
return a + b
}
var c = foo( 2 )
总的理解下来,感觉 RHS
类似 getter
,LHS
类似 setter
作用域就是一套规则,用于确定在何处以及如何查找变量(标识符)。如果找到的目的是对变量进行赋值,那么就会进行 LHS
;如果目的是获取变量的值,就会使用 RHS
查询
JavaScript 有七种内置类型
用 typeof 查看值的类型
typeof undefined === "undefined" // true
typeof true === "boolean" // true
typeof 42 === "number" // true
typeof 42 === "number" // true
typeof "42" === "string" // true
typeof {} === "object" // true
typeof Symbol() === "symbol" // true
typeof null === "object" // true
typeof function a(){} === "function" // true
typeof [1,2,3] === "object" // true
function 是 object 的子类型,它有一个内部属性[[ call ]],使得它可以被调用
数组也是 object 的子类型
其他非正式的绑定
函数不带任何修饰的函数引用(上下文)进行调用的。
function foo {
console.log(this.a)
}
var a = 2;
foo() // 2
但是如果使用严格模式,则不能将全局对象用于默认绑定,因此 this
会绑定到 undefined
function foo {
"use strict"
console.log(this.a)
}
var a = 2;
foo() // TypeError: this is undefined
兼容
function foo {
console.log(this.a)
}
var a = 2;
(function {
"use strict";
foo() // 2
})()
这一条考虑的是调用位置是否有上线对象
function foo(){
console.log(this.a)
}
var obj2 = {
a: 10,
foo: foo
}
var obj1 = {
a: 20,
foo: foo
}
obj1.obj2.foo(); // 10
如果这么思考的话,可以认为默认绑定的上下文是window
function foo {
console.log(this.a)
}
var a = 2;
foo() // 上下文是 window,这里可以看做 window.foo()
在全局作用域中声明 foo
,实际上等于 声明window.foo
function foo(){
console.log(this.a)
}
var obj = {
a: 10,
foo: foo
}
var bar = obj.foo;
var a = "oops, global";
bar(); // "oops, global"
此时的bar()
,实际上是不带任何修饰函数调用,因此使用默认绑定
call, apply 等绑定
首先重新定义 JavaScript
中的构造函数。
在JavaScript中,构造函数指的是一些使用 new
操作符时被调用的函数。他们并不属于某个类,也不会实例化一个类。实际上,他们甚至不能说是一种特殊的函数类型,他们只是被 new
操作符调用的普通函数
使用new
来调用函数,或者说发生构造函数调用时候,会自动执行下面的操作
作用域有如下几种
源码源码,拒绝无耻搬砖,当伸手党
用一个简单粗俗的例子来理解 JavaScript 的作用域
以 var a = 2
为例,分解步骤为
词法单元
,然后将词法单元解析成一个树结构。var a
,编译器会询问作用域是否已经有一个该名称的变量存在于同一个作用域的集合中。如果是,编译器会忽略该声明,继续进行编译;否则它会要求作用域在当前作用域的集合中声明一个新的变量,并命名为 a
a = 2
这个赋值操作,引擎运行时会首先询问作用域,在当前的作用域集合中是否存在一个叫做 a
的变量,如果是,引擎使用这个变量;如果否,引擎继续查找该变量,如果最终找到了 a
变量,就会将 2 赋值给它。否则引擎随手啪给你扔一个异常总之就是,变量的赋值操作会执行两个动作,首先编译器会在当前作用域中声明一个变量(如果之前没有声明过),然后在运行时引擎会在作用域中查找该变量,如果能找到就对它赋值。
不成功的 RHS
引用会导致抛出 ReferenceError
异常,不成功的LHS
引用会导致自动隐式地创建一个全局变量(非严格模式下),该变量使用LHS
引用的目标作为标识符,或者抛出ReferenceError
(严格模式下)
如果 RHS
查询找到了一个变量,但是尝试对这个变量进行不合理的操作,比如试图对一个非函数类型的值进行函数调用,或者引用 null
or undefined
类型的值中的属性,那么引擎会抛出另外一种类型的异常,叫做TypeError
ReferenceError
同作用域判别失败相关
TypeError
代表作用域判别成功了,但是对结果的操作是非法或者不合理的
tips
严格模式下禁止自动或隐式地创建全局变量
程序执行一段源代码之前会经历三个步骤
例如 var a = 2
,会被分解成 var、a、=、2
这个过程是将词法单元流(数组)转换成由一个元素逐级嵌套所组成的代表了程序语法结构的树,称为抽象语法树(Abstract Syntax Tree, AST)
例如 var a = 2
的抽象语法树有一个叫做 VariableDeclaration
的顶级节点,接下来是 Identifier
的子节点(它的值是a),以及一个叫 AssignmentExpression
的子节点。AssignmentExpression
节点有一个叫做 NumericLiteral
的子节点(它的值是2)
将 AST 转换为可执行代码的过程被称为代码生成
。
抛开具体细节,简单的来说就是有某种方法可以将 var a = 2
的AST转化为一组机器指令,用来创建一个叫做 a
的变量(包括分配内存),并将一个值存储在 a
中
JavaScript 学习到一定的基础,总是想打破瓶颈提升到下个阶段,这时候研究 JavaScript 的底层也许可以让你打破这个瓶颈。
AST 平时业务很少用到,但是当你不止于想做一个工程师,而是想当一个大神,大牛,别人口中传颂的大师级人物,或者小牛也可以,写成类似 Vue/React/Webapck 这样的工具时,那你必须懂 AST。
在 JavaScript 世界中,AST 可以认为是最底层的东西,再往下,就是转换和编译的"黑魔法"领域。
let、const声明的变量,暴露在全局,为什么没挂载到window下?究竟挂载到哪里去了?
我们打开控制台,输入
const a = 123;
function abcd() {
console.log(a); // abc函数的作用域能访问到a
};
dir(abcd);
可以在 [[Scopes]] 属性中
const、let 这类都是属于“Declarative Environment Records” 声明性环境记录,和函数、类这些一样,在单独的存储空间,var这类,属于“object environment record”,会挂载到某个对象上,也会沿着原型链去向上查找
说明不挂载到对象上,但是在一个上下文的方法中,可以访问到let、const 声明记录
foo (); // 报 ReferenceError 而不是 TypeError
var foo = function (){
// ...
}
这时候 var foo 被提升了,但是 foo并没有赋值,所以 foo() 实际上是对 undefined值进行 函数调用而导致非法操作,因此抛出 TypeError
foo() // TypeError
bar() // RefenrenceError
var foo = function bar() {
// ...
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.