重学Javascript - 极客时间《重学前端》笔记

编程语言的一般规律是: 用一定的词法和语法,表达一定的语义,从而操作运行时

Javascript文法(词法+语法)
词法 Lexical
第一类:标识符
第二类:常数
第三类:保留字 Token (不同语言的词法分析主要是这里不同)
第四类:界符  ‘/*’、‘//’、 () { } [ ] " "  ' 等
第五类:运算符 <、<=、>、>=、=、+、-、*、/、^、等
 
 

语法

这里讲了语法分析的过程。主要是AST

https://www.cnblogs.com/fundebug/p/how-does-javascript-compile.html


Javascript语义

MDN里可以查询各种语义的使用方式  (关于具体语法的使用,建议查看权威网站,比如MDN,W3等)

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript

 
Javascript运行时 - 数据结构(类型+实例)
类型
Javascript有7种语言类型
1)Undefined 
任何变量在赋值之前都是Undefined类型,值为undefined.  undefined是一个变量而非是一个关键字,这是一个设计失误。建议用void 0代替。
2)Null
这是一个关键字
3)Boolean
4)String
String的意义并非“字符串”,而是字符串的UTF16编码, 字符串的最大长度实际上是受字符串的编码长度影响的。
“因为计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理。最早的计算机在设计时采用8个比特(bit)作为一个字节(byte),所以,一个字节能表示的最大的整数就是255(二进制11111111=十进制255),0 - 255被用来表示大小写英文字母、数字和一些符号, 这个编码表被称为ASCII编码,比如大写字母A的编码是65,小写字母z的编码是122。
如果要表示中文,显然一个字节是不够的,至少需要两个字节,而且还不能和ASCII编码冲突,所以,中国制定了GB2312编码,用来把中文编进去。
类似的,日文和韩文等其他语言也有这个问题。为了统一所有文字的编码,Unicode应运而生。Unicode把所有语言都统一到一套编码里,这样就不会再有乱码问题了。
Unicode通常用两个字节表示一个字符,原有的英文编码从单字节变成双字节,只需要把高字节全部填为0就可以。
常见的有UTF16和UTF8,Unicode的码点通常这样来表示,U+0000 - U+FFFF,被称为基本字符区域BMP
通过javascript进行UTF-8编码
5)Number  双精度浮点型
非整数的Number类型无法用== (===也不行)来比较,比如在JS中, 0.1+0.2不等于0.3,除非这样写:
Math.abs (0.1+0.2 - 0.3) <= Number.EPSILON
检查等式左右两边差的绝对值是否小于最小精度,才是正确的比较浮点数的方法
6)Object
7)Symbol
这个东西主要用来干嘛? -- 最初的目的是为了实现私有属性. 后来, They are now known as unique symbols and their only intended use is to avoid name clashes between properties
 
除了这七种语言类型,还有一些语言的实现者更关心的规范类型。
List和Record: 用于描述函数传参过程
Set: 主要用于解释字符集等
Completion Record: 用于描述异常、跳出等语句执行过程
Reference: 用于描述对象属性访问、delete等
Property Descriptor: 用于描述对象的属性
Lexical Environment和Environment Record: 用于描述变量和作用域
Data Block: 用于描述二进制数据
 
 
类型转换
Number, String, Boolean, Symbol这几个基本类型,都在对象类型中有一个“亲戚”。 比如,我们必须认识到 3 与 new Number(3) 是完全不同的值,它们一个是Number类型,一个是对象类型。
 
点(.)运算符提供了装箱操作,它会根据基础类型构造一个临时对象,使得我们能在基础类型上调用对应对象的方法。但装箱机制会频繁产生临时对象,在一些对性能要求较高的场景下,我们应该尽量避免装箱操作。
 
StringToNumber
多数情况下,Number 是比 parseInt 和 parseFloat更好的方法。比如
a = Number('123')
console.log(a+1)
 
NumberToString用的很少,就不讲了

PS:  “==”运算试图实现跨类型的比较,但这是一个失误,很多编程规范中禁止使用“==”,而要求程序员进行显式地类型转换后,用===比较
识别对象对应的基本类型最准确的方法是 Object.prototype.toString().call() 比 instanceof还准确
var o = "123";
console.log(Object.prototype.toString.call(o)); //[object String]

装箱机制会频繁产生临时对象,在一些对性能要求较高的场景下,我...

极客时间版权所有: https://time.geekbang.org/column/article/78884

 
下面这篇文章对JS中的类型转换表述的比较清楚
https://www.jianshu.com/p/b161aeecb6d6
 
 
实例 - 即Javascript中的对象的原理
对象的三个特点 - 唯一标识性,状态,行为
在JavaScript中,将状态和行为统一抽象为“属性”。尤其是在JavaScript中,函数也是一种属性,跟其他属性没有区别。
JavaScript中的对象具有高度的动态性,因为JavaScript赋予了使用者在运行时为对象添改状态和行为的能力。
为了提高抽象能力,JavaScript提供了 数据属性访问器属性两类
对JavaScript来说,属性并非只是简单的名称和值,JavaScript用一组 特征来描述属性
比如数据属性,就有这四个特征  value, writable(可否被赋值), enumerable(可否被枚举), configurable
PS:  在Google的编程规范当中不建议使用访问器属性,更推荐的方式是This.
 
下面的例子展示了如何动态添加属性
 var o = { a: 1 };
 Object.defineProperty(o, "b", {value: 2, writable: false, enumerable: false, configurable: true});
 //a 和 b 都是数据属性,但特征值变化了
 Object.getOwnPropertyDescriptor(o,"a"); // {value: 1, writable: true, enumerable: true, configurable: true}
 Object.getOwnPropertyDescriptor(o,"b"); // {value: 2, writable: false, enumerable: false, configurable: true}
 o.b = 3;
 console.log(o.b); // 2
 
那什么是原型呢?  看下面的例子
var cat = {
    say(){
        console.log("meow~");
    },
    jump(){
        console.log("jump");
    }
}

var tiger = Object.create(cat,  {
    say:{
        writable:true,
        configurable:true,
        enumerable:true,
        value:function(){
            console.log("roar!");
        }
    }
})
var anotherCat = Object.create(cat);
anotherCat.say();
var anotherTiger = Object.create(tiger);
anotherTiger.say();
 
"基于原型的面向对象"和“基于类的面向对象”,前者的代表是JavaScript,  后者的代表是Java.   原型法的思想是“照猫画虎”,类的思想是“分类”。
由于原型系统的强大,使得JavaScript又可以 模拟“基于类的面向对象”。比如下面的例子:
 
用构造器模拟类的两种方法
1)第一种方法是直接在构造器中修改 this,给 this 添加属性
function c1(){
    this.p1 = 1;
    this.p2 = function(){
        console.log(this.p1);
    }
}
var o1 = new c1;
o1.p2();

2)第二种方法是修改构造器的 prototype 属性指向的对象,它是从这个构造器构造出来的所有对象的原型
function c2(){
}
c2.prototype.p1 = 1;
c2.prototype.p2 = function(){
    console.log(this.p1);
}

var o2 = new c2;
o2.p2();
 
ES6中的类
class Rex {
    constructor(name,age) {
        this.name = name;
        this.age = age;
    }
   
    get area() {
        return this.name;
    }

    sayHelloToRex() {
        console.log("hello Rex");
    }
}

var rex = new Rex('Wang Chen Long',36);
rex.sayHelloToRex();
console.log(rex.area);
 
通过括号和大括号来创建方法,数据型成员最好写在构造器里面, 通过this关键字来访问
 
Javascript中的对象分类
宿主对象 (行为完全由宿主环境决定且遵循权威标准,比如JS标准,W3C标准)-- 通常是浏览器
固有的宿主对象,比如window  https://developer.mozilla.org/zh-CN/docs/Web/API/Window
用户可创建的宿主对象,比如

比如 document.createElement 就可以创...

极客时间版权所有: https://time.geekbang.org/column/article/80011

document.createElement就可以创建一些dom对象
下面的例子直接把 <p> 元素写到 HTML 文档输出中:
<script>
document.write("<p>我的第一段 JavaScript</p>");
</script>
 
内置对象
-- 第一类,固有的内置对象,在任何 JS 代码执行前就已经被创建出来了,可以通过ECMA标准查看
https://www.ecma-international.org/ecma-262/9.0/index.html#sec-well-known-intrinsic-objects
也可以用程序去查找,查找的标准是对象属性的描述符
[Function: eval]
[Function: isFinite]
[Function: isNaN]
[Function: parseFloat]
[Function: parseInt]
[Function: decodeURI]
[Function: decodeURIComponent]
[Function: encodeURI]
[Function: encodeURIComponent]
[Function: Array]
[Function: Date]
[Function: RegExp]
[Function: Promise]
[Function: Proxy]
[Function: Map]
[Function: WeakMap]
[Function: Set]
[Function: WeakSet]
[Function: Function]
[Function: Boolean]
[Function: String]
[Function: Number]
[Function: Symbol]
[Function: Object]
{ [Function: Error] stackTraceLimit: 10 }
[Function: EvalError]
[Function: RangeError]
[Function: ReferenceError]
[Function: SyntaxError]
[Function: TypeError]
[Function: URIError]
[Function: ArrayBuffer]
[Function: SharedArrayBuffer]
[Function: DataView]
[Function: Float32Array]
[Function: Float64Array]
[Function: Int8Array]
[Function: Int16Array]
[Function: Int32Array]
[Function: Uint8Array]
[Function: Uint16Array]
[Function: Uint32Array]
[Function: Uint8ClampedArray]
Object [Atomics] {}
Object [JSON] {}
Object [Math] {}
{}
[]
[Function: isArray]
[Function: from]
[Function: of]
Date {}
[Function: now]
[Function: parse]
[Function: UTC]
RegExp {}
[Function: get input]
[Function: set input]
[Function: get $_]
[Function: set $_]
[Function: get lastMatch]
[Function: set lastMatch]
[Function: get $&]
[Function: set $&]
[Function: get lastParen]
[Function: set lastParen]
[Function: get $+]
[Function: set $+]
[Function: get leftContext]
[Function: set leftContext]
[Function: get $`]
[Function: set $`]
[Function: get rightContext]
[Function: set rightContext]
[Function: get $']
[Function: set $']
[Function: get $1]
[Function: set $1]
[Function: get $2]
[Function: set $2]
[Function: get $3]
[Function: set $3]
[Function: get $4]
[Function: set $4]
[Function: get $5]
[Function: set $5]
[Function: get $6]
[Function: set $6]
[Function: get $7]
[Function: set $7]
[Function: get $8]
[Function: set $8]
[Function: get $9]
[Function: set $9]
Promise {}
[Function: all]
[Function: race]
[Function: resolve]
[Function: reject]
[Function: revocable]
Map {}
WeakMap {}
Set {}
WeakSet {}
[Function]
[Boolean: false]
[String: '']
[Function: fromCharCode]
[Function: fromCodePoint]
[Function: raw]
[Number: 0]
[Function: isFinite]
[Function: isInteger]
[Function: isNaN]
[Function: isSafeInteger]
Symbol {}
[Function: for]
[Function: keyFor]
{}
[Function: assign]
[Function: getOwnPropertyDescriptor]
[Function: getOwnPropertyDescriptors]
[Function: getOwnPropertyNames]
[Function: getOwnPropertySymbols]
[Function: is]
[Function: preventExtensions]
[Function: seal]
[Function: create]
[Function: defineProperties]
[Function: defineProperty]
[Function: freeze]
[Function: getPrototypeOf]
[Function: setPrototypeOf]
[Function: isExtensible]
[Function: isFrozen]
[Function: isSealed]
[Function: keys]
[Function: entries]
[Function: values]
Error {}
[Function: captureStackTrace]
[EvalError]
[RangeError]
[ReferenceError]
[SyntaxError]
[TypeError]
[URIError]
ArrayBuffer {}
[Function: isView]
SharedArrayBuffer {}
DataView {}
Float32Array {}
Float64Array {}
Int8Array {}
Int16Array {}
Int32Array {}
Uint8Array {}
Uint16Array {}
Uint32Array {}
Uint8ClampedArray {}
[Function: load]
[Function: store]
[Function: add]
[Function: sub]
[Function: and]
[Function: or]
[Function: xor]
[Function: exchange]
[Function: compareExchange]
[Function: isLockFree]
[Function: wait]
[Function: wake]
[Function: notify]
[Function: parse]
[Function: stringify]
[Function: abs]
[Function: acos]
[Function: acosh]
[Function: asin]
[Function: asinh]
[Function: atan]
[Function: atanh]
[Function: atan2]
[Function: ceil]
[Function: cbrt]
[Function: expm1]
[Function: clz32]
[Function: cos]
[Function: cosh]
[Function: exp]
[Function: floor]
[Function: fround]
[Function: hypot]
[Function: imul]
[Function: log]
[Function: log1p]
[Function: log2]
[Function: log10]
[Function: max]
[Function: min]
[Function: pow]
[Function: random]
[Function: round]
[Function: sign]
[Function: sin]
[Function: sinh]
[Function: sqrt]
[Function: tan]
[Function: tanh]
[Function: trunc]
[Function: defineProperty]
[Function: deleteProperty]
[Function: apply]
[Function: construct]
[Function: get]
[Function: getOwnPropertyDescriptor]
[Function: getPrototypeOf]
[Function: has]
[Function: isExtensible]
[Function: ownKeys]
[Function: preventExtensions]
[Function: set]
[Function: setPrototypeOf]
[Function: concat]
[Function: find]
[Function: findIndex]
[Function: pop]
[Function: push]
[Function: shift]
[Function: unshift]
[Function: slice]
[Function: splice]
[Function: includes]
[Function: indexOf]
[Function: keys]
[Function: entries]
[Function: forEach]
[Function: filter]
[Function: map]
[Function: every]
[Function: some]
[Function: reduce]
[Function: reduceRight]
[Function: toString]
[Function: toLocaleString]
[Function: join]
[Function: reverse]
[Function: sort]
[Function: lastIndexOf]
[Function: copyWithin]
[Function: fill]
[Function: values]
[Function: toString]
[Function: toDateString]
[Function: toTimeString]
[Function: toISOString]
[Function: toUTCString]
[Function: getDate]
[Function: setDate]
[Function: getDay]
[Function: getFullYear]
[Function: setFullYear]
[Function: getHours]
[Function: setHours]
[Function: getMilliseconds]
[Function: setMilliseconds]
[Function: getMinutes]
[Function: setMinutes]
[Function: getMonth]
[Function: setMonth]
[Function: getSeconds]
[Function: setSeconds]
[Function: getTime]
[Function: setTime]
[Function: getTimezoneOffset]
[Function: getUTCDate]
[Function: setUTCDate]
[Function: getUTCDay]
[Function: getUTCFullYear]
[Function: setUTCFullYear]
[Function: getUTCHours]
[Function: setUTCHours]
[Function: getUTCMilliseconds]
[Function: setUTCMilliseconds]
[Function: getUTCMinutes]
[Function: setUTCMinutes]
[Function: getUTCMonth]
[Function: setUTCMonth]
[Function: getUTCSeconds]
[Function: setUTCSeconds]
[Function: valueOf]
[Function: getYear]
[Function: setYear]
[Function: toJSON]
[Function: toLocaleString]
[Function: toLocaleDateString]
[Function: toLocaleTimeString]
[Function: exec]
[Function: get dotAll]
[Function: get flags]
[Function: get global]
[Function: get ignoreCase]
[Function: get multiline]
[Function: get source]
[Function: get sticky]
[Function: get unicode]
[Function: compile]
[Function: toString]
[Function: test]
[Function: then]
[Function: catch]
[Function: finally]
[Function: get]
[Function: set]
[Function: has]
[Function: delete]
[Function: clear]
[Function: entries]
[Function: forEach]
[Function: keys]
[Function: get size]
[Function: values]
[Function: delete]
[Function: get]
[Function: has]
[Function: set]
[Function: has]
[Function: add]
[Function: delete]
[Function: clear]
[Function: entries]
[Function: forEach]
[Function: get size]
[Function: values]
[Function: delete]
[Function: has]
[Function: add]
[Function]
[Function: apply]
[Function: bind]
[Function: call]
[Function: toString]
[Function: toString]
[Function: valueOf]
[Function: anchor]
[Function: big]
[Function: blink]
[Function: bold]
[Function: charAt]
[Function: charCodeAt]
[Function: codePointAt]
[Function: concat]
[Function: endsWith]
[Function: fontcolor]
[Function: fontsize]
[Function: fixed]
[Function: includes]
[Function: indexOf]
[Function: italics]
[Function: lastIndexOf]
[Function: link]
[Function: localeCompare]
[Function: match]
[Function: normalize]
[Function: padEnd]
[Function: padStart]
[Function: repeat]
[Function: replace]
[Function: search]
[Function: slice]
[Function: small]
[Function: split]
[Function: strike]
[Function: sub]
[Function: substr]
[Function: substring]
[Function: sup]
[Function: startsWith]
[Function: toString]
[Function: trim]
[Function: trimStart]
[Function: trimEnd]
[Function: toLowerCase]
[Function: toUpperCase]
[Function: valueOf]
[Function: toLocaleLowerCase]
[Function: toLocaleUpperCase]
[Function: toExponential]
[Function: toFixed]
[Function: toPrecision]
[Function: toString]
[Function: valueOf]
[Function: toLocaleString]
[Function: toString]
[Function: valueOf]
[Function: __defineGetter__]
[Function: __defineSetter__]
[Function: hasOwnProperty]
[Function: __lookupGetter__]
[Function: __lookupSetter__]
[Function: isPrototypeOf]
[Function: propertyIsEnumerable]
[Function: toString]
[Function: valueOf]
[Function: get __proto__]
[Function: set __proto__]
[Function: toLocaleString]
[Function: toString]
[Function: get byteLength]
[Function: slice]
[Function: get byteLength]
[Function: slice]
[Function: get buffer]
[Function: get byteLength]
[Function: get byteOffset]
[Function: getInt8]
[Function: setInt8]
[Function: getUint8]
[Function: setUint8]
[Function: getInt16]
[Function: setInt16]
[Function: getUint16]
[Function: setUint16]
[Function: getInt32]
[Function: setInt32]
[Function: getUint32]
[Function: setUint32]
[Function: getFloat32]
[Function: setFloat32]
[Function: getFloat64]
[Function: setFloat64]
[Function: getBigInt64]
[Function: setBigInt64]
[Function: getBigUint64]
[Function: setBigUint64]
wake {}
notify {}

 
-- 第二类,“特权对象”。 在上面150+的固有内置对象中,我们把能够通过语言本身的构造器创建的对象称作“原生对象”,也叫“特权对象”
通过这些构造器, 我们可以用 new 运算创建新的对象,几乎所有这些构造器的能力都是无法用纯JavaScript代码实现的,它们也无法用class/extend语法来继承。所以,它们其实是为了某些特定场景(比如使用“下标”)而设计出来的,也就是有“特权”。

 
用对象来模拟函数与构造器:函数对象与构造器对象
JavaScript 为这一类对象预留了私有字段机制
函数对象的定义是:具有[[call]]私有字段的对象,具有[[construct]]私有字段的对象

JavaScript 用对象模拟函数的设计代替了一般编程语言的函数,它们可以像其他语言的函数一样被调用,传参。任何宿主只要提供了“具有[[call]]私有字段的对象”,就可以被JavaScript函数调用语法支持。
https://developer.mozilla.org/zh-CN/docs/Web/ JavaScript/Reference/Global_Objects/Function/call
 
宿主对象或者内置对象,在作为函数调用和作为构造器使用的情况下,行为是不一样的
比如内置对象 Date 在作为构造器调用时产生新的对象,作为函数时则产生字符串
 

 
Javascript运行时 - 执行过程(算法)
事件循环
一个 JavaScript 引擎会常驻于内存中,当宿主遇到一些事件时,就会把JavaScript代码或者函数传递给它执行
在ES3和更早的版本中,JavaScript本身还没有异步执行代码的能力,当宿主环境传递给JavaScript引擎一段代码,引擎就把代码直接顺次执行了,这个任务也就是宿主发起的任务。在ES5之后,JavaScript引入了Promise, 这样,不需要浏览器的安排,JavaScript引擎本身也可以发起任务了。
 
Promise的总体思想是,需要进行io,等待或者其他异步操作的函数,不返回真实结果,而返回一个“承诺”
函数的调用方可以在合适的时机,选择等待这个承诺兑现(通过Promise的then方法的回调)
基本用法如下:
function sleep(duration) {
 return new Promise(function(resolve, reject){
 setTimeout(resolve,duration);
 });
}
sleep(3000).then(()=> console.log('finished'));

NodeJS中的process.nextTick方法可以在当前"执行栈"的尾部----下一次Event Loop(主线程读取"任务队列")之前----触发回调函数。也就是说,它指定的任务总是发生在所有异步任务之前。setImmediate方法则是在当前"任务队列"的尾部添加事件,也就是说,它指定的任务总是在下一次Event Loop时执行,这与setTimeout(fn, 0)很像


根据JSC引擎的术语, 宿主发起的叫做宏观任务,JavaScript引擎本身发起的叫做微观任务
在JS引擎底层的C/C++代码中,事件循环是一个跑在独立线程中的循环,
while(true) {
   r = wait();
    execute(r);
}
整个循环做的事情基本上就是反复“等待-执行”。
 
这里有篇文章讲的不错  http://www.ruanyifeng.com/blog/2014/10/event-loop.html
 
只有异步代码会进入任务队列。 虽然上图中只有一个任务队列,但我认为把它分解成 宏任务队列微任务队列有更强的解释力。 比如, Promise会添加到微任务队列末尾。setTimeout等宿主API, 则会添加到宏任务队列末尾。队列总是先进先出,且微任务总是优先于宏任务,除非有定时器
 
下面这个例子证明了,“只有异步代码会进入任务队列”,“微任务优先于宏任务”
setTimeout(()=>console.log("d"), 0);
var r = new Promise(function(resolve, reject){
 console.log("a"); /*虽然这句写在Promise中,但它不是异步代码, 不会添加到任务队列中 */
 resolve();
});

r.then(() => console.log("c"));
console.log("b");
下面这个例子证明了,“两个队列”,“队列总是先进先出”
setTimeout(()=>console.log("d"), 0);
var r = new Promise(function(resolve, reject){
 resolve()
});
r.then(() => { 
 console.log("c1"); 
 new Promise(function(resolve, reject){
 resolve()
 }).then(setTimeout(()=>console.log("c2"),0));
});
async/await
async函数必定返回Promise, 我们把所有返回Promise的函数都可以认为是异步函数
async函数强大的地方在于可以嵌套
 
下面的例子展示了如何嵌套。我们可以看到,如果把sleep这样的异步操作放入某一个框架或者库中,使用者几乎不需要了解Promise的概念即可进行异步编程了
 
 
题目: 让一个div改变颜色,绿色3秒,黄色1秒,红色2秒  这里必须用异步,如果不用异步模式就看不到颜色的变化了
Ps:  这个例子使用了JQuery,  只有JQuery可以用.style,   document本身是没有style属性的
<script>
                $(document).ready(function(){
                function changeTrafficLight(color, duration) {
                        return new Promise(function(resolve, reject) {
                        document.getElementById("traffic-light").style.backgroundColor = color;
                        setTimeout(resolve, duration);
                        })
                    }
             
                async function trafficScheduler() {
                        await changeTrafficLight("green", 3000);
                        await changeTrafficLight("yellow", 1000);
                        await changeTrafficLight("red", 2000);
                        trafficScheduler();
                    }
             
                trafficScheduler();
            });
    </script>
函数的执行

我们可以这样简单理解一下,闭包其实只是一个绑定了执行环境的函...

极客时间版权所有: https://time.geekbang.org/column/article/83302

闭包
我们可以这样简单理解一下,闭包其实只是一个绑定了执行环境的函数,它与普通函数的区别是,它携带了执行的环境,就像人在外星中需要自带吸氧的装备一样,这个函数也带有在程序中生存的环境
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures
 
JavaScript中的闭包包括下面这些部分:
环境部分
环境:函数的词法环境(执行上下文的一部分)
标识符列表:函数中用到的未声明的变量
表达式部分:函数体
实际上 JavaScript 中跟闭包对应的概念就是“函数”,不是执行上下文,也不是作用域
 
那什么是执行上下文? 什么又是作用域呢?
  所谓的“执行上下文”,就是说同样的代码在不同的位置,会绑定不同的上下文,从而产生不同的行为。

最主要的还是词法环境和变量环境。 词法环境是实现闭包的必需条件。
VAR变量环境的例子如下:
 
 
所谓作用域链, 就是指一个函数激活执行的时候去哪儿找变量的值。它也是实现闭包的必需条件。

 

class animal {
 constructor(name) {
 this.name = name;
 }

}

class dog extends animal {
 constructor(name){
 this.name = name
 }
}

function createEatFunction() {
 var desc = " is eating";
 return function eat(animal){
 console.log(animal.name+desc);
 };
}

var eat = createEatFunction();
//全局变量
var desc = "正在吃东西";
eat(dog); //这里输出什么?

函数调用切换上下文
七种函数。区别在于this关键字的行为。
1)普通函数
function foo(){
    // code
}
2) 箭头函数
const foo = () => {
    // code
}
3)方法。在class中定义的函数
class C {
    foo(){
        //code
    }
}
4) 生成器函数。用function*定义的函数。
function foo*(){
    // code
}
5) 类。用class定义的函数。实际上也是函数。
class Foo {
    constructor(){
        //code
    }
}
6,7,8) 异步函数:普通函数、箭头函数和生成器函数加上 async 关键字。
async function foo(){
    // code
}
const foo = async () => {
    // code
}
async function foo*(){
    // code
}
 
This的运行机制 - this是执行上下文中很重要的一个组成部分。同一个函数调用方式不同,得到的this值也不同
JavaScript标准定义了[[thisModel]]私有属性。
[[thisModel]]属性的三个取值
lexical: 表示从上下文中找this, 这对应了箭头函数
global:表示当this为undefined时,取全局对象,对应了普通函数
strict:当严格模式时使用,this严格按照调用时传入的值,可能为null或者undefined
 
无论是否在严格模式下,在全局执行环境中(在任何函数体外部)this 都指向全局对象。
在函数内部,this的值取决于函数被调用的方式。
1) 不在严格模式下,且 this 的值不是由该调用设置的,所以 this 的值默认指向全局对象。
2) 所以,在严格模式下,如果 this 没有被执行环境(execution context)定义,那它将保持为 undefined
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/this
 
我们看一个例子
function showThis(){
    console.log(this);
}

var o = {
    showThis: showThis
}

showThis(); // global
o.showThis(); // o
 
如果改成箭头函数,结果就不一样了。
const showThis = () => {
    console.log(this);
}

var o = {
    showThis: showThis
}

showThis(); // global
o.showThis(); // global

如果改成“方法”,结果又不一样了
class C {
    showThis() {
        console.log(this);
    }
}
var o = new C();
var showThis = o.showThis;

showThis(); // undefined
o.showThis(); // C的实例o

 
为什么This有这样的行为呢? 因为 在 JavaScript 标准中,为函数规定了用来保存定义时上下文的私有属性[[Environment]]
当一个函数执行时,会创建一条新的执行环境记录,记录的外层词法环境会被设置成函数的[[Environment]]
 
在下面的例子中, foo 能够访问 b(定义时词法环境),却不能访问a (执行时的词法环境),这就是执行上下文的切换机制
```js
// 这是 foo.js 文件里的代码
var b = 2;
module.exports = function() { // 导出function
  console.log(b);
  console.log(a);
};
```
```js
// 这是test.js 文件里的代码
var foo = require("./foo.js"); // 引入function 为foo
var a = 1;
foo();
// node 执行 test.js 输出:
// -> 2
// -> ReferenceError: a is not defined
 
当函数调用时,会入栈一个新的执行上下文,函数调用结束时,执行上下文会出栈

Function.prototype.call和Function.prototype.apply可以指定函数调用时传入的this值
function foo(a, b, c){
    console.log(this);
    console.log(a, b, c);
}
foo.call({}, 1, 2, 3);
foo.apply({}, [1, 2, 3]);
 
MDN中关于call和apply的详细解释
 
 
JavaScript语句的执行
(这部分内容了解就好。个人感觉没有太多实际意义。不过将来可以参照这种方式去分析别的语言的语句执行)
JavaScript 语句执行机制涉及的一种基础类型:Completion Record类型
function foo(){
  try{
    return 0;
  } catch(err) {

  } finally {
    console.log("a")
  }
}

console.log(foo());

JavaScript 正是依靠语句的 Completion ...

极客时间版权所有: https://time.geekbang.org/column/article/83860

JavaScript正是依靠语句的Completion Record类型,方才可以在语句的复杂嵌套结构中,实现各种控制。
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements
 

带标签的语句 -- 任何JavaScript语句都是可以加标签的,在语句前加冒号即可。
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Loops_and_iteration
 
 

猜你喜欢

转载自www.cnblogs.com/wangclwh/p/10424678.html
今日推荐