JS忍者秘籍读书笔记(已完结)

1、函数也可以添加属性

利用计算素数的例子来说明:可以通过函数的属性来缓存结果,提升性能。
这里是函数的属性,用this的话会绑定到window上,函数创建默认this为window

function isPrime(value) {
    
	 if (!isPrime.answers) {
    
	 	isPrime.answers = {
   }; 
	 } 
	 if (isPrime.answers[value] !== undefined) {
    
		 return isPrime.answers[value]; 
	 } 
	 var prime = value !== 0 && value !== 1; // 1 is not a 素数
	 for (var i = 2; i < value; i++) {
    
	 	if (value % i === 0) {
    
	 		prime = false; 
	 		break; 
	 	} 
	 } 
 	return isPrime.answers[value] = prime; 
}

2、var、let、const

var 是在距离最近的函数全局词法环境中定义变量。

let 和 const 在最近的词法环境中定义变量(可以是在块级作用域内、循环内、函数内或全局环境内,循环其实就是一个块级作用域)

3、js代码的执行过程

代码运行的两个阶段:

一旦创建了新的词法环境

执行第一阶段不执行代码,扫描所有代码
JavaScript 引擎会访问并注册在当前词法环境中所声明的变量和函数。

具体的处理过程如下:
1.先创建形参与参数默认值:如果是创建一个函数环境,那么创建形参及函数参数的默认值。非函数环境跳过此步骤。

2.声明函数:扫描当前代码进行函数声明。
1)如果是创建全局或函数环境,就扫描当前代码进行函数声明(不会扫描其他函数的函数体),但是不会执行函数表达式或箭头函数。
2)对于所找到的函数声明,将创建函数,并绑定到当前环境与函数名相同的标识符上。若该标识符已经存在,那么该标识符的值将被重写(同名函数被覆盖)。如果是块级作用域,将跳过此步骤。

3.声明变量:扫描当前代码进行变量声明。
1)在函数或全局环境中,查找所有当前函数以及其他函数之外(就是不在函数体里的)通过 var 声明的变量,并查找所有通过 let 或 const 定义的变量。
2)在块级环境中,仅查找当前块中通过 let 或 const 定义的变量。
3)对于所查找到的变量,若该标识符不存在,进行注册并将其初始化为 undefined。若该标识符已经存在,将保留其值(这就是为什么变量覆盖不了函数声明,因为函数声明已经占用了这个标识符)。

第二阶段执行代码
具体如何执行取决于变量的类型(let、var、const 和函数声明)以及环境类型(全局环境、函数环境或块级作用域)

在程序的实际执行过程中,跳过了函数声明部分,而不是简单的认为把它提升了

补充知识:
1)无论何时创建函数,都会保持词法环境的引用(通过内置[[Environment]]属性)

2)每次调用函数时均会创建新的执行上下文并推入执行栈。这同时引起创建新的词法环境,词法环境通常用于保持跟踪函数中定义的变量。

3)this 表示函数上下文,即与函数调用相关联的对象。函数的定义方式和调用方式决定了 this 的取值。



4、生成器与迭代器

生成器四个状态
● 挂起开始——创建了一个生成器,其中的任何代码都未执行。

● 执行——生成器中的代码已执行。执行要么是刚开始,要么是从上次挂起的时
候继续的。当生成器对应的迭代器调用了 next 方法,并且当前存在可执行的代
码时,生成器都会转移到这个状态。

● 挂起让渡——当生成器在执行过程中遇到了一个 yield 表达式,它会创建一个包
含着返回值的新对象,随后再挂起执行。生成器在这个状态暂停并等待继续执行。

● 完成——在生成器执行期间,如果代码执行到 return 语句或者全部代码执行完
毕,生成器就进入该状态。

特点:当我们从生成器中取得控制权后,生成器的执行环境上下文一直是保存的,而不是像标准函数一样退出后销毁

1)生成器定义

function* WeaponGenerator() {
    
	 yield "Katana"; 
	 yield "Wakizashi"; 
	 yield "Kusarigama"; 
}
// 生成器执行 就等于生成了一个 迭代器
WeaponGenerator()

2)for of循环可以取出每一步运行的值

for (let weapon of WeaponGenerator()) {
    
 	console.log(weapon)
}

3)for of其实就是语法糖

const weaponsIterator = WeaponGenerator()
let item
while(!(item = weaponsIterator.next()).done) {
    
	 console.log(item)
}

4)生成器与递归遍历dom节点

function* DomTraversal(element){
    
	 yield element // 返回当前元素
	 element = element.firstElementChild // 换成子元素,可能为空
	 while (element) {
    
		 yield* DomTraversal(element)
		 element = element.nextElementSibling
	 } 
}

const subTree = document.getElementById("subTree")
for(let element of DomTraversal(subTree)) {
    
  console.log(element)
} 

5)向生成器传值:
迭代器.next(xxxx) 这里的xxx参数就是生成器中,当前挂起的yield后表达式的返回值,
当然不能通过这样初始化,初始化可以直接在声明迭代器的时候传参

function* NinjaGenerator(action) {
    
	 const imposter = yield ("Hattori " + action); 
	 assert(imposter === "Hanzo", "The generator has been infiltrated"); 
 	yield ("Yoshi (" + imposter + ") " + action); 
}
const ninjaIterator = NinjaGenerator("skulk"); // 初始化传值

6)向生成器抛出异常(也是种传值)

function* NinjaGenerator() {
    
	 try{
    
		 yield "Hattori"; 
		 fail("The expected exception didn't occur");  // 这里的代码不会执行
	} 
	catch(e){
    
		 assert(e === "Catch this!", "Aha! We caught an exception"); 
 	} 
} 
const ninjaIterator = NinjaGenerator(); 
const result1 = ninjaIterator.next(); 
assert(result1.value === "Hattori", "We got Hattori"); 
ninjaIterator.throw("Catch this!");

7)next方法不会创建新的执行上下文到执行栈,而是激活当前的生成器执行上下文

const result1 = ninjaIterator.next();

习题中的反省:
1)for of中获取生成器的值时,return的值不会被使用,详情见上方while循环代码,因为判断出当前的状态是done时,就已经停止了,不会运行循环中的处理函数。

下面代码只会push第一个个值

function* NinjaGenerator(){
    
	 yield "Yoshi"; 
	 return "Hattori"; 
	 yield "Hanzo"; 
} 
var ninjas = [];
for(let ninja of NinjaGenerator()){
    
 	ninjas.push(ninja); 
}

2)当前.next()中转入的参数是上一个yield的返回值

function* Gen(val) {
    
	 val = yield val * 2; 
	 yield val; 
} 
let generator = Gen(2);
let a1 = generator.next(3).value; // 4   这里传入的3会被忽略
let a2 = generator.next(5).value; // 是5 !!!!!!!!!!!!!!!!!!!!!!!!!!!!


5、Promise

可以使用替代预发来处理拒绝 promise,通过使用内置的 catch 方法

promise.then(()=> fail("Happy path, won't be called!"))
.catch(() => pass("Promise was also rejected")); 
//then里面只写了resolve后的执行方法,链式调用.catch来处理错误

如果在处理promise时出现末处理的异常,则会被隐式地拒绝,catch和error回调都可以捕获到

const promise = new Promise((resolve, reject) => {
    
 	undeclaredVariable++;  // 未定义变量
});
promise.then(() => fail("Happy path, won't be called!")) 
 .catch(error => pass(
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值