You Dont Know JS(上)笔记

最近项目,一直在研究使用新的框架,新的工具,结果发现学习的速度还没有轮子出现的速度快,就拿webpack来说,1.0都还没有掌握,2.0就出来了,无力感突现,一直在跟随着别人的脚步在前进,都没有自己的东西,所以内功才是一切,内功强大,自己写一个框架,也不是不可。不然你连人家的源码都不懂,只会用又有什么用呢,明天换一个框架,或者换一个公司,你又要从头开始,你的竞争力又在何方。

  • 作用域

1,LHS(赋值操作)和RHS(取值操作)查询都会在当前执行作用域开始,如果没有找到,就会向上级作用域继续查找目标标识符,这样每次上升一级,最后抵达全局作用域。
2,不成功的RHS引用会抛出ReferenceError异常,不成功的LHS引用会导致自动隐式的创建全局变量(非严格模式下),或者抛出ReferenceError异常(严格模式下)
  • 词法作用域##

词法作用域完全由写代码期间函数声明的位置来定义

function foo(a) {
    var b = a * 2;
    function bar(c) {
        console.log( a, b, c );
    }

    bar(b * 3);
}
foo( 2 ); // 2 4 12

对于console而言,会先查找最内部的作用域,引擎在这里无法找到a,因此就会去上一层作用域中查找。作用域查找会在找到第一个匹配的标识符时停止,所以在多层嵌套的作用域中可以定义同名的标识符,这叫做遮蔽效应(内部的标识符遮蔽了外部的标识符)

function foo(str, a) {
    eval( str ); // 欺骗!,在运行时就相当于var b= 3,遮蔽了外部的b
    // 但是在严格模式下,eval运行时,有自己的作用域,无法修改所在的作用域
    console.log( a, b );
}
var b = 2;
foo( "var b = 3;", 1 ); // 1 3
function foo(obj) {
    with (obj) {
        a = 2;
    }
}
var o1 = {
    a: 3
};
var o2 = {
    b: 3
};
foo( o1 );
console.log( o1.a ); // 2
foo( o2 );
console.log( o2.a ); // undefined
console.log( a ); // 2 -- 不好,a被泄露到全局作用域上了!
//当我们传递o1给with时,with所声明的作用域是o1,而这个作用域中含有一个相符的标识符,但是我们将o2作为作用域时,其中并没有a标识符,因此进行了正常的LHS查找

使用eval和with可以欺骗词法作用域,但是会导致性能的下降,所以不要使用,同时因为js引擎会在编译阶段进行数项的优化,但是对于未知的代码时优化不了的

  • 函数作用域和块作用域

1,在软件设计中,应该最小限度的暴露必要内容,而将其他的内容都隐藏起来,比如某个模块或对象的API,
2,(function(){})()和(function(){}())是一样的东西,都叫做立即执行函数表达式(IIFE),看个人习惯,
3,在try/catch的catch分局中会创建一个作用域
4,
  • 提升

1,js存在预编译阶段,此时会将函数声明和变量提升,并且函数会在变量之前,同事若是重复的声明将会忽略
  • 闭包

1,无论通过何种手段将内部函数传递到所在的词法作用域以外,他都会持有对原始定义作用域的引用,无论在何处执行这个函数都会使用闭包,内部的变量都不会销毁,因为闭包需要使用,最常见的就是回调函数,和setTimeout,
2,延迟函数的回调会在循环结束后才执行,会先把当前的同步执行完,
3,现在大多数的模块依赖加载/管理器的本质上都是将这种模块定义封装进一个友好的API中,一下是核心的原理
var MyModules = (function Manager(){
  var modules = []
  // name: 模块名称
  // deps:模块依赖
  // impl: 模块实体
  function define(name,deps,impl){
    // 得到所有依赖,当前依赖的东西必须在之前已经定义好的
    for(var i=0;i
4,模块文件中的内容会被当做好像包含在作用域闭包中一样来处理。
  • this

使用new来调用函数,会自动执行下面的操作
1,创建一个全新的对象
2,这个对象会被执行[[Prototype]]连接
3,这个新对象会绑定到函数调用的this
4,如果函数内部没有返回,那么自动返回这个新对象
1,字符串,数值,布尔字面量在需要的时候会自动转换为对应的对象
2,object.assign使用=复制,所以引用的东西复制过来还是引用
3,var myobject  = Object.create(anotherobject)//以这个对象为原型创建一个对象
var obj = Object.create(null)得到的对象没有_proto_,存储数据最好了。
  • 原型,委托

这里的原型概念确实拓展了以前的理解
常见的类的实现

function Foo(who){
    this.me = who
}
Foo.prototype.identify = function(){
    return 'I am '+ this.me
}
function Bar(who){
    Foo.call(this,who)
}
// 惭愧,以前直接=,因为是引用,所以不合逻辑
Bar.prototype = Object.create(Foo.prototype)
Bar.prototype.speak = function(){
    alert('Hello,'+ this.identify()+'.')
}
var b1 = new Bar('b1')
var b2 = new Bar('b2')
b1.speak()
b2.speak()

这种实现情况下,他的关系图如下(良心出品):

You Dont Know JS(上)笔记_第1张图片
传统实现关系图.png

1,因为bar的原型对象中没有constructor所以去原型链中找到了Foo,b1,b2也是一样
2,Foo,Bar都是Function 的一个实例(函数也是一个对象),包括function Object()也是,所以他们的constructor都指向Function 对象,而 proto都指向Function.prototype,

使用委托风格实现的代码:

Foo = {
  init: function(who){
    this.me = who
  },
  identify: function(){
    return 'I am '+this.me
  }
}

Bar = Object.create(Foo)

Bar.speak = function(){
  alert('Hello,'+this.identify()+'.')
}

var b1 = Object.create(Bar)
b1.init('b1')

var b2 = Object.create(Bar)
b2.init('b2')

b1.speak()
b2.speak()

关系图如下:


You Dont Know JS(上)笔记_第2张图片
委托实现关系图.png

实现方式比较简单,就是一路的使用原型链去委托,找不到的话,就去上一级寻找。
还是习惯于使用传统的方式,但是委托的方式还是很大拓展的思维

  • 严格模式的限制

变量必须声明后再使用
函数的参数不能有同名属性,否则报错
不能使用with语句
不能对只读属性赋值,否则报错
不能使用前缀0表示八进制数,否则报错
不能删除不可删除的属性,否则报错
不能删除变量delete prop,会报错,只能删除属性delete global[prop]
eval不会在它的外层作用域引入变量
eval和arguments不能被重新赋值
arguments不会自动反映函数参数的变化
不能使用arguments.callee
不能使用arguments.caller
禁止this指向全局对象
不能使用fn.caller和fn.arguments获取函数调用的堆栈
增加了保留字(比如protected、static和interface)
  • es6模块

1,代码是在模块作用域之中运行,而不是在全局作用域运行。模块内部的顶层变量,外部不可见。
2,模块脚本自动采用严格模式,不管有没有声明use strict。
3,模块之中,可以使用import命令加载其他模块(.js后缀不可省略,需要提供绝对 URL 或相对 URL),也可以使用export命令输出对外接口。
4,模块之中,顶层的this关键字返回undefined,而不是指向window。也就是说,在模块顶层使用this关键字,是无意义的。
5,同一个模块如果加载多次,将只执行一次。

你可能感兴趣的:(You Dont Know JS(上)笔记)