javascritp文章 You-Dont-Know-JS
https://github.com/getify/You-Dont-Know-JS
有6个系列。git在线免费。
第一本, up and going (点击链接)
Mission:
作者建议在开始学习时,learn 'just enough' to get by。无需了解语言行为的how and why。
之后,路变的rough,深度学习理解所有的JavaScript。
作者implore reader,学习'The Tough Parts'。
作者challenge you to joureny down the bumpy 'road less traveled'.
当你深挖这个系列,你会真正懂JavaScript。
Summary
JavaScript is awesome. 部分好懂,更多的难懂。
当开发者遭遇困惑,他们通常责备这个语言,实际上是他们理解上的欠缺。
本书会搞定这些难点。
Chapter 3: Into YDKJS
摘要:
- Scope & Closures
- (最基础的内容。Hoisting)
- (先理解Scope,才能理解Closures)
- (module pattern需要最优先理解)
- this & Object Protoytpes
- (4个简单rule来理解和决定this的绑定)
- 和this紧密相关的是Prototypes机制。它在行为上和类完全是对立的。
- behavior delegation, 行为委托。是Prototypes机制的理解。反类和继承的。
- Types & Grammar
- (type coercion, 类型强制转换,作者认为是非常有用和被低估的工具)
- Async & Performance
- 前3个title是核心机制,Async branch out(偏离核心,扩展范围)
- Asynchrony不仅是程序执行的关键,它在可写性和可维护性上,作为关键因素上不断的增长。
- 本章开始:清理概念困惑,如异步,平行,一致性。
- 然后:异步的主要方法,回调。并阐明它的缺陷。并讲解ES6介绍的2个新机制:
- promises, generators
- Promises: 一个时间独立的包裹器,关于一个未来value。(防止回调地狱)
- Generators: 一个新的执行JS函数的mode。一个pause-resume能力, 让在生成器中的看着是同步/序列的code,实际上在scenes后面异步的执行。
- 处理non-linear非线性, non-local-jump 回调的困惑。因此让我们的异步代码看起来是同步的(sync-looing),以便更合理的reason-able。
- 结论,在异步的世界有效率的编程,,你需要真的去适应把promises和generators联合起来。
- ES6 & Beyond
- JavaScript进化的速度非常快。你需要不断探索。
"You Don't Know JS" isn't a criticism or an insult.
It's a realization that all of us, myself included, must come to terms with.
Learning JavaScript isn't an end goal but a process.
We don't know JavaScript, yet. But we will!
Chapter1: Into Programming
statement声明
a = b * 2; //2是literal value。字面量值。 ;是semicolon冒号。用在大多数声明后面。
Expressions
Statements由一个/多个expressions组成。
a = b * 2;
- a是literal expression
- b是一个变量expression, 意思是:取它的当前值
- b*2是数学expression, 意思是: 做乘法multiplication
- a = b*2是一个assignment expression。
一个普遍的表达式也被称为an expression statement。 b * 2
一个更普遍的表达式声明是 a call expression statement(Function)。 alert(a);
练习
小技巧:在浏览器console,多行输入用<shift> + 回车。
经常忘记:
console.log()是console对象使用log()方法。
window.prompt()方法:弹出输入框,输入的值被储存在age中。
age = prompt('Please tell me your age:');
console.log(age);
Object.a a是property属性。
Converting Between Types
coercion:把一个类型转化为另外一个类型。
var a = "42"; var b = Number(a);
implicit coercion含蓄的强制转化。(Chapter2会讲到这个coercion)
//JavaScrip会转化左侧的string为number.
"99.99" == 99.99 true
Code Comments
strive努力写执行正确的代码,也要保证检查时容易读。好的变量/函数名字。
好的评论代码也很重要。
- 一定要有评论
- 不要每行都有,适量。
- 评论的作用是explain why, not what。如果代码比较复杂,需要explain how。
// comments
/* comments */
Chapter2 Into JavaScript3
对之后5章的概览。少量concepts。
Values & Types
string
number
boolean
null
andundefined typeof null返回object这是bug
object
symbol
(new to ES6) typeof symbo返回的也是字符串。
var a = '123'
typeof a
的意思是当前在a内的值的类型,“type of the value currently in a”
undefined 未定义的。
- var a , 没有赋值,输入a返回的就是undefined.
- 函数,return no values, 返回的也是undefined
- 使用void operator也返回undefined
- statment: 如 var a = '123', 返回的也是undefined。代表没有返回值。return no values。
注意:const声明的是变量,但不能改变type类型。
Objects
Object type 涉及了a compund value。复合值。 property/value, 属性/值对儿。
var obj = { "hello world": "Thanks", b: 42, c: true } => undefined
obj =>{hello world: "Thanks", b: 42, c: true}
obj["hello world"] =>"Thanks"
用复合词作为对象的属性,需要加""
⚠️:
obj["b"] //42,
属性在[]中必须加上冒号,代表他是obj的属性。而不是一个变量。
反面教材:👇
var obj = {
a: "hello world",
b: 42
};
var b = 'a'
obj["b"] //42
obj[b] //"hello world", 因为b是一个变量,所以obj[b]等同obj["a"]
Array和Funciton可以看成是object类型的子类型。特定的版本。
Array
使用数组作为索引。
Functions
类型是funciton
function foo() { return 42; } foo.bar = "hello world"; typeof foo; // "function" typeof foo(); // "number" typeof foo.bar; // "string"
Built-in Type Methods
就是通过Object prototypes,使用了方法。
Comparing Values
Coercion分 explict和implicit。
尤其是implicit coercion.
var a = "42"var b = a*1 b =>42 a =>"42"
Truthy & Falsy
从非bollean value 转化为boolean的原则:以下都是falsey.
""
(empty string)0
,-0
,NaN
(invalidnumber
)null
,undefined
false
equality: == ===
注意:
1. ==会强制转化左遍的值。
2. 非原生值, 如object(包括array,function)。它们是被引用的by reference。小心 == 和===
a和b指向同一块内存空间,自身不能比较自身。返回false
var a = [1,2,3]; var b = [1,2,3]; var c = "1,2,3"; a == c; // true b == c; // true a == b; // false
Inequality
JS string values 可以被比较。
Variables
JS内, 变量名必须是验证的识别符号.
An identifier 的第一符号需要是a
-z
, A
-Z
, $
, or _
.其中之一。
另外一些关键字不能作为indetifier.
Function Scopes
Hoisting: 在一个scope的任意位置声明一个变量(声明函数也一样),这个变量就属于这个作用域,可以在这个作用域任意位置中存取。(相当于把声明放到作用域第一行。)
注意:
- var声明,约定写在作用域内的首行,但函数可以先用后定义。
- 不要使用非正式的声明 a = 1, 因为非严格模式下,a会自动放入全局作用域。导致各种潜在问题!!
ES6增加了块作用域和let声明,在块作用域中声明的let变量只能在声明的块内使用。
Conditionals
- if...else...if
- switch () { }
- ternary 三元表达式。
Strict Mode
ES5提供了严格模式。应该始终使用严格模式。让你的代码更安全,更优化optimizable。
//可以在函数内使用严格模式
function foo() { "use strict"; }
//也可以在.js文件开始使用'use strict'
特点:
- 不再允许省略var声明变量, 否则会❌ 'var' missing, ReferenceError
-
function foo() { "use strict"; a = 1; } foo() Uncaught ReferenceError: a is not defined
⚠️:如果你在严格模式下,代码报错,证明你的程序需要fix。
Functions As Values
函数本身就是一个value,可以分配给变量,或者传递给其他函数,从其他函数返回。
同样,一个函数值应当被看成是一个expression
function foo() {...} //声明函数 var foo = function() {...} // 给foo分配一个未命名anonymous的函数
var x = function bar() {...} //给x分配一个有名字的函数
Immediately Invoked Function Expressions (IIFEs)
立即执行的函数表达式:
和普通函数区别:
function foo() { .. }
// 先声明函数,foo是函数名字,用()括号来执行foo函数
foo(); // `IIFE` function expression, // then `()` executes it
// 在一个()内声明IIFE函数,然后在用()括号执行这个函数。()()
(function IIFE(){ .. })();
Closure
最重要但很少被理解的concepts。它是一个非常重要的技术,in JavaScripts。
闭包类似一个方式,记住并继续存取一个函数的作用域(中的变量),即使函数已经完成运行。
function makeAdder(x) {
function add(y) {
return y + x;
}
return add; //返回add函数本身,而不是add函数的运行结果
}
var plusOne = makeAdder(1); //变量plusOne的值就是函数add,但其中x的值是1.
plusOne(3); //4, 1加3
在控制台输入: plusOne.pototype得到:
其中constructor内部包括了👇的作用域内容:
//分为函数作用域和全局作用域:
[[Scopes]]: Scopes[2]
0: Closure (makeAdder) {type: "closure", name: "makeAdder", object: {…}}
1: Global {type: "global", name: "", object: Window}
// 函数makeAdder的作用域是闭包Closure,储存了内部变量x = 1
也就是说,plusOne的值add()函数, 可以得到makeAdder()函数作用域中的变量x。
Modules
在JavaScript中,普遍的使用closure的模式,就是Module pattern。
可以作为一个公共的API使用。
注意⚠️:这里没有使用new User()。 User()是一个函数,不是一个等待被实例化的类。
function User(){ var username, password; function doLogin(user,pw) { username = user; password = pw; // do the rest of the login work } var publicAPI = { login: doLogin }; return publicAPI; } // 创建一个User Module实例。一个新的作用域被创建,因此拷贝了内部的这些变量/函数。
// fred储存的是这个实例。
// 如果在运行一次User(), 则得到新的实例,和fred无关。
var fred = User(); fred.login( "fred", "12Battery34!" );
内部函数doLogin()有一个closure,包括了username和password, 意味着它保存了对这两个变量的存取权,即使User()函数已经完成运行。
publicAPI是一个对象。它的login属性引用了内部函数doLogin()。当我们return publicAPI, publicAPI成为了这个实例,我们叫做fred。
因为内部函数doLogin(),变量username, password仍然在keep alive,所以fred.login(..), 等同于调用内部函数doLogin()。它可以存取这2个内部变量。
this Identifier
如果一个函数有一个this引用在内部,this引用通常指向一个object。但是this指向哪个对象要看函数如何调用。
⚠️,重要的是, this不涉及函数自身。这是最常常弄混淆的地方。
4个rules: examine how the function was called!!!
function foo() { console.log( this.bar ); } var bar = "global"; var obj1 = { bar: "obj1", foo: foo }; var obj2 = { bar: "obj2" }; // -------- foo(); // "global" obj1.foo(); // "obj1",obj1对象的属性foo的值储存foo函数。 foo.call( obj2 ); // "obj2", obj2对象被函数foo调用。 new foo(); // undefined
第一行:
在non-strict mode, foo()执行后,this指向全局对象,比如window对象。
在严格模式,this会显示: undefined并得到一个❌提示。
第二行/第三行: 设置this指向obj1对象/obj2对象
第四行: new foo()设置this给 a brand new empty object, 一个空的对象。
Prototypes
比较复杂。对象如果自身没有一个属性,它会通过prototype, 原型链去找这个属性,并返回值或undefined.
behavior delegation, 而不是模仿类
var foo = { a: 42 }; // create `bar` and link it to `foo` var bar = Object.create( foo ); bar.b = "hello world"; bar.b; // "hello world" bar.a; // 42 <-- delegated to `foo`
不是很清楚,如何让一个对象的pototype中放入一个对象。
Old & New 浏览器对新feature的支持
Polyfilling:
used to refer to taking the definition of a newer feature and producing a piece of code that's equivalent to the behavior, but is able to run in older JS environments.
Transpiling
如何让新的功能,适应旧的浏览器。一种变通的方式。(不看。)
Non-JavaScript
你写的代码的很大一块不是直接由JavaScript控制的。例如:
最常用的非JS javaScript 是DOM API,如:
document是一个全局变量。一个特殊的object, 被称为host object
本书不会涉及非JS JavaScript机制的内容。