你不知道的javascript上卷

作用域

javascript是一门编译语言,它不是提前编译的,编译结果也不能在分布式系统中移植。编译的步骤一般如下:

  • 分词/词法分析 词法分析是有状态的判断一个分词是一个独立的词法单元还是其他词法单元的一部分,调用有状态的解析规则。
  • 解析/语法分析 抽象语法结构树(AST)
  • 代码生成 将AST转换为可执行代码的过程

对javascript来说,大部分情况下编译发生在代码执行前的几微秒,javascript引擎不会有像其他语言编译器那么多的时间来进行优化。
编译器(语法分析、代码生成)-引擎(负责程序的编译及执行过程)-作用域(负责收集交维护由所有声明的标识符组成的一系列查询,确定标识符的访问权限)
引擎执行的查找会影响最终的查找结果。
RHS查询与简单地查找某个变量的值。
LHS查询则是试图找到变量的容器本身,从而可以对其赋值。
赋值操作的目标是谁(LHS)以及谁是赋值操作的源头(RHS)。

词法作用域

每一个函数都会创建一个新的作用域气泡。内部标识符会“遮蔽”外部标识符。
无论何种情况,eval(…)都可以在运行时期修改书写期的词法作用域。如果代码中大量使用eval或with,那么运行一定会变得非常慢。

函数作用域和块作用域

属于这个函数的全部变量都可以在整个函数的范围内使用及复用。
库通常会在全局作用域中声明一个名字足够独特的变量,通常是一个对象。这个对象用作库的命名空间,所有需要暴露给外界的功能都会成为这个对象的属性。
包装函数的声明以(function…而不是以function…开始,包装函数会被当作函数表达式而不是标准的函数声明来处理,通常在末尾加另外一个()可以立即执行这个函数,这种模式叫IIFE。
匿名函数表达式function()..,书写简单快捷,缺点是调试、递归和可读性差。
let关键字可以将变量绑定到所在的任意作用域中,通常是{…}内部,但let声明不会在块作用域中进行提升。

提升

编译阶段中的一部分工作就是找到所有的声明,并用合适的作用域将它们关联起来,所括变量和函数在内的所有声明都会在任何代码被执行前首先被处理。声明本身会被提升,函数声明会被提升,但函数表达式不会被提升,且函数会首先提升,然后才是变量。

作用域闭包

当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行的。回调就是典型的闭包的例子。
延迟函数的回调会在循环结束时才执行,需要注意。

for (let i=1; i<=5; i++) {setTimeout(function timer() {console.log(i);}, i*1000);
}

模块需要具备的条件:

  • 必须有外部的封闭函数,该函数必须至少被调用一次(每次调用都会创建一个新的模块实例)。
  • 封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问或者修改私有状态。
  • 现代模块机制
 var MyModules = (function Manager() {var modules = {};function define(name, deps, impl) {for (var i=0; i<deps.length; i++) {deps[i] = modules[deps[i]];}modules[name] = impl.apply( impl, deps );}function get(name) {return modules[name];}return {define: define,get: get};
})();
MyModules.define("bar", [], function() {function hello(who) {return "Let me introduce: " + who;}return {hello: hello};
} );
MyModules.define("foo", ["bar"], function(bar) {var hungry = "hippo";function awesome() {console.log( bar.hello( hungry ).toUpperCase() );}return {awesome: awesome};
} );
var bar = MyModules.get("bar");
var foo = myModules.get("foo");
console.log( bar.hello("hippo") );//Let me introdue: hippo
foo.awesome();//LET ME INTRODUCE: HIPPO

关于this

this被自动定义在所有函数的作用域中,它是在运行时进行绑定的,它的上下文取决于函数调用时的各种条件。

绑定规则

  • 默认
    如果使用严格模式,则不能将全局对象用于默认绑定,因此this会绑定到undefined
  • 隐式
    隐式绑定规则会把函数调用中的this绑定到这个上下文对象。
    javascript
    function foo() {
    console.log( this.a );
    }
    var obj2 = {
    a: 42,
    foo: foo
    };
    var obj1 = {
    a: 12,
    obj2: obj2
    };
    obj1.obj2.foo(); //42

    隐式绑定的函数会丢失绑定对象
    javascriopt
    function foo() {
    console.log( this.a );
    }
    var obj = {
    a: 2,
    foo: foo
    };
    var bar = obj.foo;
    var a = "oops, global";
    bar(); //"oops, global"

    bar 是obj.foo的一个引用,但实际上,它引用的是foo函数本身,因此此时的bar()其实是一个不带任何修饰的函数调用,因此应用了默认绑定。
  • 显式
    call(..)和apply(..)
    第一个参数对象是给this准备的。
  • 硬绑定

    function bind(fn, obj) {return function() {return fn.apply( obj, arguments );
    };
    }

    ES5提供了内置方法Function.prototype.bind

  • new
    对于函数的“构造调用”,会自动执行如下步骤:
    1.创建一个全新的对象
    2.这个新对象会被执行[[Prototype]]连接
    3.这个新对象会绑定到函数调用的this
    4.如果函数没有返回其他对象,那么new表达式中的函数会自动返回这个新对象。
    硬绑定>new
    显示>隐式
    new>隐式
    在javascript中创建一个空对象最简单的方法就是Object.create(null),和{}很像,但是并不会创建Object.prototype这个委托,所以它比{}更空。
    函数的间接引用,调用这个函数里会应用默认绑定规则。
    箭头函数的绑定无法被修改。

对象

引擎会自动把字面量转换成内置对象,所以可以访问属性和方法。null和undefined没有对应的构造形式,它们只有文字形式,而Date只有构造没有文字形式。
在对象中属性名永远都是字符串。

Object.getOwnPropertyDescriptor(myObject, "a");
Object.defineProperty(myObject,"a",{value: 2,writable: true, //是否可以以修改属性的值configurable: true, //只要属性是可配置的,就可以使用defineProperty(..)enumerable: true //属性是否会出现在对象的属性枚举中,如for..in循环
} );

属性不变性
- 对象常量 writable:false和able:false就可以创建一个真正的常量属性
- 禁止扩展 Object.preventExtensions( myObject )
- 密封 Object.seal(..)调用Object.preventExtensions()+configurable:false
- 冻结 Object.freeze(..)调用Object.seal()+writable:false

in操作符会检查属性是否在对象及其[[Prototype]]原型链中,hasOwnProperty(..)
for..of遍历属性值

“类”

javascript中的函数无法真正地复制,所以只能复制对共享函数对象的引用。

function Foo() {//...
}
var a = new Foo();
Object.getPrototypeOf( a) === Foo.prototype; //true

原型继承常常被视为动态语言版本的类继承。继承意味的复制操作,javascript并不会复制对象属性,相反会在两个对象之间创建一个关联,这样一个对象就可以通过委托访问另一个对象的属性和函数。
a.constructor === Foo意味着a确实有一个指向Foo的.constructor属性,但实际上,.constructor(不可枚举)引用被委托给了Foo.prototype,而Foo.prototype.constrcutor默认指向Foo。
可以渲染序列图:

Created with Raphaël 2.1.0a1a1Foo.prototypeFoo.prototypea2a2Bar.prototypeBar.prototypeb1b1b2b2

原型继承

function Foo(name) {this.name = name;
}
Foo.prototype.myName = function() {return this.name;
}
function Bar(name,label) {Foo.call(this, name);this.label = label;
}
//我们创建了一个新的Bar.prototype对象并关联到Foo.prototype
Bar.prototype = Object.create( Foo.prototype );
//注意!这里没有Bar.prototype.constructor了
//如果你需要这个属性的话需要手动修复一下它
Bar.prototype.myLabel = function() {return this.label;
};
var a = new Bar("a","obj a");
a.myName(); //"a"
a.myLabel(); //"obj a"
function inherits(Child, Parent) {var F = function(){};F.prototype = Parent.prototype;Child.prototype = new F();Child.prototype.construction = Child;
}

委托控件对象

var Widget = {init: function(width, height){this.width = width || 50;this.height = height || 50;this.$elem = null;},insert: function($where) {if (this.$elem) {this.$elem.css( {width: this.width + "px",height: this.height + "px"} ).appendTo( $where );}}
};
var Button = Object.create( Widget );
Button.setup = function(width, height, label) {//委托调用this.init( width, height );this.label = label || "Default";this.$elem = $( "<button>" ).text( this.label );
};
Button.build = function($where) {//委托调用this.insert( $where );this.$elem.click( this.onClick.bind( this ));
};
Button.onClick = function(evt) {console.log( "Button'" + this.label + "' clicked!" );
};
$( document ).ready( function() {var $body = $( document.body );var btn1 = Object.create( Button );btn1.setup(125, 30, "Hello");var btn2 = Object.create( Button );btn2.setup(150, 40, "World");btn1.build($body);btn2.build($body);

你不知道的javascript上卷相关推荐

  1. 【你不知道的JavaScript上卷】——作用域与闭包

    原文: [你不知道的JavaScript上卷]--作用域与闭包 JS语言万变不离其宗,其中最常用.最重要的也就是常用的几个大概念.数据类型.作用域.原型链.闭包.this指针.异步,不同的人理解不一样 ...

  2. JS闭包—你不知道的JavaScript上卷读书笔记(二)

    关于闭包,初学者会被绕的晕头转向,在学习的路上也付出了很多精力来理解. 让我们一起来揭开闭包神秘的面纱. 闭包晦涩的定义 看过很多关于闭包的定义,很多讲的云里雾里,晦涩难懂.让不少人以为闭包是多么玄乎 ...

  3. 你不知道的JavaScript 上卷 Part1

      这篇博客躺在我的草稿箱里有一阵子了,差点给遗忘了哈哈. 前言   最近开始喜欢读一些书,从书中找答案,在阅读中查漏补缺.   记得小学初中时候最爱看书了,如今却不知怎的,习惯性从网络中摄取知识,搜 ...

  4. 《你不知道的JavaScript上卷》知识点整理与读书笔记

    各位路过的的大佬.求关注.求点赞.谢谢 第一部分 作用域和闭包 第1章 作用域是什么 1.1编译原理 1.2理解作用域 1.3作用域嵌套 1.5异常 第2章 词法作用域 2.1词法阶段 2.2欺骗词法 ...

  5. 你不知道的JavaScript 上卷读书笔记

    看了<你不知道的JavaScript 上>,为了防止自己忘记,特此记下与我而言的部分重点 任何足够先进的技术都和魔法无异. --Arthur C. Clarke 作用域和闭包 编译原理 分 ...

  6. 200530你不知道的JavaScript[上卷]所有小结汇总

    JS上卷整理 说点啥 1504的书,现在(2005)才想起好好看,过去5年零1个月了,证明自己的技术能力真是水了5年多.抓紧补齐吧. S11 表示 <不知道系列> 上卷 第一部分 第一章 ...

  7. 你不知道的JavaScript上卷(一)

    第一章 作用域 存储和访问变量,几乎是所有编程语言的基本功能之一.但如何将变量引入,如何存储,如何查找等这些问题,就需要一套设计良好的规则进行管理.这套规则则被称为作用域 1.1编译原理 尽管通过将J ...

  8. 你不知道的JavaScript上卷-作用域和闭包

    1. LHS引用与RHS引用的区别: RHS:取到源值-得到某某的值 LHS:谁是赋值操作的源头-给谁赋值 function foo(a) {var b = a;return a + b; } var ...

  9. #你不知道的javascript上卷# 总结

    上卷看完了 全程下来:晦涩难懂,收货颇丰 虽然具体回忆倒不知道看了什么知识点,但是会感觉眼前的代码亮堂了很多 这本书最大的特点就是:读它得跟读文言文一样,一句话得需要好几遍再加上思考才会明白 有好几次 ...

最新文章

  1. FFmpeg中拉取rtsp视频流并缩放显示测试代码
  2. 用 ASP.NET 开发 Web 服务的五则技巧
  3. 云炬Android开发笔记 15评价晒单功能实现(自定义评分控件和仿微信自动多图选择控件)
  4. java+spring+mysql配置_JAVA后台搭建(springboot+mybatis+mysql)项目搭建
  5. vue中dom元素和组件的获取
  6. Pyqt5转化.ui文件到.py文件
  7. Socket Programming
  8. memcpy-avx-unaligned/strcpy_sse2_unaligned崩溃记录
  9. android无线充电技术,无线充电Qi通信协议分析,充电qi通信协议
  10. Esxi 6.7安装教程
  11. 机器学习在各个领域的实际应用
  12. python中将奇数和偶数分列
  13. CTWAP和CTNET的区别
  14. 聚焦存储即平台,浪潮存储迎来发展新机遇
  15. cad怎么画立体图形教学_怎么在CAD中绘制三维立体图
  16. 项目管理之项目成本管理
  17. 遥感影像基于样本的面向对象分类方法
  18. trove 基本介绍
  19. 深度揭秘Xshell后门事件:入侵感染供应链软件的大规模定向攻击
  20. 华为手机疑似鸿蒙,疑似华为自研手机系统现身:名字叫鸿蒙?

热门文章

  1. 基础篇|信贷风控中的外部数据(百行)
  2. Moonlight Shadow
  3. Dundas Chart for WinForms 企业版+专业版 v7.1.0.1812 for VS2008 (含注册机)
  4. 网友:看到 955 不加班的公司名单,我酸了...
  5. 从三尺讲台到三尺空间,在线教育的“真互动”时代来了
  6. 如何运用matlab给球面染上颜色,matlab画球体颜色如何选择
  7. Untiy CurvedUI 的使用的bug修正
  8. TCA云架构工程师认证试题
  9. mysql中从分离_Mysql主从分离
  10. 学校计算机网络教室中常用的数据传输介质是,2012年计算机一级MsOffice第四十一套练习题及答案解析...