豆瓣评分:9.4
GitHub开源书籍
JavaScript进阶必读书籍
第一部分作用域和闭包
第一章:作用域是什么
JavaScript有一个预编译的过程,
两个知识点:LSH、RHS查询。
LHS左查询,找到变量,如果没有找到,会在全局热心创建一个,如果是严格模式,就报错。(赋值符号的左边就是LHS查询)
RHS右查询, 取得变量的值,如果没有找到会报错referenceError。(获取变量的值,或者执行函数就是RHS查询)
作用域: 存储变量找到变量的规则。
第二章:词法作用域
欺骗语法:
动态生成代码:动态生成代码并不推荐使用
1、因为它带来的好处无法吊销性能上的损失。
2、eval还会受严格模式影响。
两个语法:
eval(“var b = 10”) 根据传入的字符串动态变量。
with(“对象参数”){}
第三章:函数作用域与块作用域
区分函数声明和表达式
function是第一个词语就是函数声明,否则是表达式。
区别:
- 函数声明会被提升到当前作用域的顶部
- 函数声明需要命名,而函数表达式可以选择命名或者使用匿名函数
一个技巧:
使用函数表达式时,应当给函数命名,报错的时候,容易快速找到位置。
第四章:提升
编译阶段的一部分工作就是找到所有的声明,并用合适的作用域将他们关联起来。
var变量和函数声明会提升
var fn = function demo(){} demo并部会提升
第五章:作用域闭包
能够访问其他函数作用域中的变量的函数就是闭包函数
当一个函数可以记住并访问它所在的词法作用域时,就产生了闭包。简单来说,闭包就是指有权访问另一个函数作用域中变量的函数。
第二部This和原型对象
两个误区:
- 指向函数自身。
如果函数内部要引用自己,可以用自己的函数名来引用,匿名函数通过arguments也可以找到自己。也可以在函数调佣的时候 fn.call(fn,.....)
- This指向函数的作用域。
函数在任意时候都不指向自己的词法作用域。
this既不指向函数自身也不指向他的词法作用域(默认情况)
this到底是什么? this是在运行时绑定的,并不是在编写时绑定的,它取决于函数调用的各种条件,与函数声明的位置没有任何关系,取决于函数调用方式。
this是运行时绑定的,它取决于函数调用时的各种条件
默认绑定,直接调用 this就是windows
隐式绑定, 通过对象调用,this就是对象
显示绑定 call、apply、bind
New 绑定。
函数的this优先级:
如果函数是new 调用,this指向实例对象
如果是通过call/apply/bind进行绑定,this指向他们的第一个参数
如果函数是通过某个对象调用,this指向这个对象
如果都是,严格模式this是undefined ,非严格模式windows
箭头函数永不修改this,默认指向在定义它时所处的对象(宿主对象),此处指父级作用域
第三章对象
1、null被当做”object” 其实是语言本身的bug。
原因对象在底层标识为二进制,在JavaScript中二进制前三位0的话会判断为object类型null的二进制表示是全0.所以typeof 时会返回object。
2、内容,对象中的属性也称之为内容, 对象的属性名永远是字符串。可以通过obj.key 或者obj[key] 访问,不同是obj.key需要符合标识符命名规范,[key]可以是任意Utf-8的字符串。
- 注意:属性名永远是字符串,使用其他类型做属性,会先转为字符串
- 数组也是对象,存储 数值/值对 可以像对象一样给数组添加属性,不影响数组的长度,不过他的键是一个数字 或者 能看起来像一个 数字 “3”,(整数)那么它就会修改数组的内容。函数是可调用的对象,可以给函数添加属性例如fn.msg= ‘函数fn’
- For in 和 会检查原型链
- obj.hasOwnProperty("key") 只会检查自己本身
- Object.create 可以理解为创建一个对象,并把这个对象的prototyep指向复制 的对象
第四章原型
JavaScript对象都有一个特殊的【prototype】的内置属性,指向他的原型对象,原型链的顶端是Object.prototype。
Prototype机制就是对象中一个内部的连接引用另一个对象
什么是原型链:
Js中对象都有一个特殊的内置属性【prototype】,指向它的原型对象,访问一个对象中不存在的属性时,就会沿着这条prototype链查找。
属性设置和屏蔽:
给对象的属性设置值时会有三种情况:
- 如果原型上有这个属性并且设置了setter,那它一定会调用这个setter
- 默认情况下是给对象添加或者修改这个属性的值
- 严格模式的话,如果对象身上没有这个属性,会报错。严格模式阻止了对未定义属性进行隐式创建的行为,以提供更严格的错误检查。
属性屏蔽是什么?
一个对象的新属性名称与其原型链上的属性名称相同,从而屏蔽了原型链上属性的访问或修改。
// 定义一个父对象
var parent = {
x: 10
};
// 创建一个子对象,它的原型指向 parent
var child = Object.create(parent);
child.x++
// child.x = child.x + 1 给子对象添加一个x属性,从而屏蔽掉了parent的x的访问
类函数(构造函数):
构造函数就是new调用的函数,JavaScript中是没有类的,然后衍生出一种滥用的模仿类,通常使用构造函数来模仿类。
执行new的步骤
1、创建一个空对象{}
2、将空对象的【prototype】属性指向 构造函数.prototype
3、执行函数体,并将this绑定到对象上
4、如果函数返回对象或者数组类型,则返回改值。否则返回创建的对象
错误观点和方式:
- let fn = new Fn() fn是由Fn构造的
- Bar.prototype = Fn.prototype 实现集成,实际上没有关联,是地址引用
- Bar.prototyp = new Fn() 建议使用Objeact.create()
关联关系:
instanceof左边是一个普通的对象,右边是一个函数。
Instanceof 回答的是在 对象的整条【prototype】链上,是否有 右边函数的.prototype指向的对象
知识点:
1、Object.create
语法:
let ojb = Object.create(要关联的对象, {
// 可选参数 配置其他属性
c: {
value: 'c'
}
})
创建一个对象,并把对象的【prototype】指向 要关联的对象
ES5之前可以使用
if(!Object.create){
Object.create = function(o){
function F(){}
F.prototype = o
return new F()
}
}
2、obj.hasOwnProperty(“key”)
判断obj身上有没有某个属性,(不会沿着原型链查找)
3、获取对象原型
Object.getPrototypeOf(obj) 获取对象的“原型对象”
还有一种方法__proto__ 在es6之前并不是官方的,但是也有大多数浏览器支持
- .constructor
对象的.constructor默认指向一个函数,而这个函数也叫做prototype的引用指向的对象,constructor并不标识对象被它构造,JavaScript的继承需要手动修改prototype.constructor的指向。【结论:.constructor并不可靠】
第一种情况:
function Fn(){
this.name = "666"
}
Fn.prototype = {
name:'测试'
}
var a = new Fn();
console.log(Fn.prototype.constructor ) // ƒ Object()
console.log(a.constructor == Fn) // false 应该是 == Objeact
第二种情况
function Fn(){
this.name = "666"
}
// 如果prototype != 赋值给一个对象也可以不用指向
Fn.prototype.constructor = Fn
var a = new Fn();
console.log(Fn.prototype.constructor == Fn) // true
console.log(a.constructor == Fn) // true
- JavaScript中的继承方式
Parent.prototype.makeMoney = function () {
console.log(this.name,'定个小目标,赚了一个亿!')
}
// 父类的构造函数
function Parent() {
this.colors = ['red', 'blue'];
}
// 子类的构造函数
function Child() {
Parent.call(this,...)
}
// 这种方式有两个坏处
// 1、会执行代码体
// 2、不够直观
Child.prototype = new Parent()
// Child.prototype = Object.create(Parent) 推荐使用
Child.prototype.constructor = Child
var c1 = new Child()
c1.colors.push("black")
var c2 = new Child()
6、ES6修改对象的【prototype】
Bar.prototype = Object.create(Fn.prototype);
let bar = new Bar()
bar.constructor.name // Object
ES6标准可靠的方式修改对象的prototype关联
Object.setPrototypeOf(Bar.prototype, Fn.prototype);
let bar = new Bar()
bar.constructor.name // Bar
7、判断对线的关联关系
instanceof左边一个对象,右边一个函数。怎么来判断两个对象的关联关系
两种方法,判断对象a是否在b的原型上
let Fn = function (){}
Fn.prototype = 对象a
console.log(对象b instanceof Fn)
console.log(对象a.isPrototypeOf(对象b))