XyyFighting


  • 首页

  • 标签

  • 分类

  • 归档

《JavaScript设计模式与开发实践》读书笔记1

发表于 2018-07-09 | 分类于 读书笔记 , JavaScript设计模式与开发实践

第1章 面向对象的JS

  • 静态类型语言:编译时确定变量的类型

  • 动态类型语言:程序运行时,变量被赋值后,才确定变量类型。(JS)

  • 利用鸭子类型思想,可以面向接口编程,而不是面向实现编程

  • 多态:静态类型语言中实现多态,需要先向上转型,但在JS中与生俱来多态,只要有该方法即可执行

  • 封装:使用变量作用域封装特性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var myObject=(function(){
    var _name='xyy';
    return {
    getName:function(){
    return _name;
    }
    }
    })();
    console.log(myObject._name); //undefined
    console.log(myObject.getName()); //xyy
  • 原型模式是一种设计模式,也是一种编程泛型

第2章 this、call、apply

  • 在使用new构造器时,如果构造器显式地返回了一个object类型的对象,就会返回这个对象。不显示返回任何数据,或者显式返回非对象数据,则不会。

  • 1
    2
    3
    4
    5
    6
    7
    // 想用一个短函数代替document.getElementById,但是this会指向window,所以可以如下这么写
    var getId = document.getElementById;
    document.getElementById = (function(func) {
    return function() {
    return func.apply(docuemnt, arguments);
    };
    })(document.getElementById);
  • ​

《你不知道的JS(中)》读书笔记6

发表于 2018-07-05 | 分类于 读书笔记 , 你不知道的JS

第5章 语法

  • 语句相当于句子,表达式相当于短语

  • 标签语句:

    • continue foo不是跳转到标签foo所在位置继续执行,而是执行foo循环的下一轮循环
    • break foo不是跳转到标签foo所在位置继续执行,而是跳出标签foo所在循环代码块,继续执行后面的语句
  • JSON-P通过将JSON数据传递给函数,将JSON转换为合法的JS语法

  • []+{} //"[object Object]"[]会被强制类型转换为””,{}会被强制类型转换为”[object Object]”

  • {}+[] //0{}空代码块,+[]显示强制类型转换为0

  • 三元运算符和=运算符是右关联的

    1
    2
    3
    4
    5
    6
    var a=42;
    var b="foo";
    var c=false;
    var d=a&&b||c?c||b?a:c&&b:a; //42

    // (a&&b||c)?((c||b)?a:(c&&b)):a
  • ASI:自动分号插入

  • 对于ES6来说,函数参数被省略或被赋值为undefined效果一样,但是arguments数组不同,一个为空,一个出现值为undefined的单元

  • 不要同时访问命名参数和其对应的arguments数组单元

  • finally中抛出异常或者有返回值,都会覆盖try和catch中的return返回值

  • switch中的case运算符是===,case的表达式即使结果为真值,但是不是严格意义上的true,则不会执行。

《你不知道的JS(中)》读书笔记5

发表于 2018-06-28 | 分类于 读书笔记 , 你不知道的JS

第3章 原生函数

  • 所有typeof返回值为“object”的对象都包含一个内部属性[[Class]],一般用Object.prototype.toString.call([1,2,3])可以得到
  • valueOf():得到封装对象中的基本类型值
  • 不要创建和使用空单元数组,应该要创建[undefiend,undefiend]这样的数组
  • Array.apply(null,{length:3})创建出[undefiend,undefiend,undefiend]
  • 常用的原生函数有:
    • String(): 一般不直接使用
    • Number():一般不直接使用
    • Boolean():一般不直接使用
    • Array():尽量不使用,不加new也行
    • Object():尽量不使用
    • Function():尽量不使用
    • RegExp():尽量不使用
    • Date():有用
    • Error():有用,不加new也行
    • Symbol():不能带new

第4章 强制类型转换

  • 类型转换:显式将值从一种类型转换为另一种类型,发生在静态类型语言的编译阶段,在代码中就可以看出
  • 强制类型转换:隐式将值从一种类型转换为另一种类型,发生在动态类型语言的运行时

ToString

  • toString是非字符串到字符串的强制类型转换
  • 对于普通对象来说,除非自定义,否则toString()返回内部属性[[class]]的值
  • JSON.stringfy(…)不是强制类型转换,但是使用到了toString方法

ToNumber

  • toPrimitive:对象转换为相应的基本类型值,首先检查valueOf()方法,再使用toString()方法的返回值

ToBoolean

  • 包装了假值的封装对象,强制转换成boolean是true

    1
    2
    3
    4
    var a=new Boolean(false);
    var b=new Number(0);
    var c=new String("");
    var d=Boolean(a&&b&&c); // true
  • 假值对象:浏览器在某些特定情况下,在常规JS语法基础上自己创建了一些外来值。比如document.all。假值对象强制类型转换为布尔型的结果为false

  • 真值无限多,记住假值

    • undefined
    • null
    • false
    • +0、-0、NaN
    • “ “

显式强制类型转换

  • a.toString()涉及隐式转换,因为基本类型(比如数字42)没有toString方法,会先创建一个封装对象,再对该对象调用toString。显式转换中含有隐式转换。
  • +c运算符显式将C转换为数字,而非数字加法运算
  • ~按位取反,相当于-(x+1)。只有-1可以让值成为0
  • 抽象渗漏:indexOf()>=0或者!=-1这样的写法不好,在代码中暴露了底层的实现细节。可以使用~indexOf()只有在结果为-1的时候结果为0
  • ~~x和x|0与Math.floor(..)效果一样,可以截除数字值的整数部分

显式解析数字字符串

  • 解析允许字符串含有非数字字符,解析会从左到右,如果遇到非数字字符就停止:parseInt
  • 转换不允许出现数字字符,否则就失败返回NaN:Number

显式转换为布尔值

  • Boolean(..)和!!,会显式强制类型转换为布尔值
  • b=a?true:false在三元运算符中,a进行了隐式强制类型转换,应该使用Boolean(..)或者!!进行显式强制类型转换

隐式强制类型转换

  • & 和 || 运算符的返回值并不一定是布尔类型,而是两个操作数其中一个的值。
  • 符号类型可以被显式转换成字符串,隐式会报错,不能转换为数字,转换为布尔值为true。

== 和 ===

  • ==允许在相等比较中进行强制类型转换,但是===不允许

  • 1
    2
    3
    var x=true;
    var y="42";
    x==y //false

    首先toNumber(x)将true转换为1,此时1===“42”,两者类型仍然不同,字符串和数字之间比较会把字符串转成数字,即1==42.

  • 只有null==undefiend才true,两者之一于其他任何值(0,false,“”)都为false

  • 特别注意:

    1
    2
    3
    4
    []==0  //true  
    ""==0 //true
    "0"==0 //true
    []=="" //true
  • 0=="\n" //true因为””、”\n”、” “等空字符串转换为数字是0

《深入React技术栈》读书笔记1

发表于 2018-06-27 | 分类于 读书笔记 , 深入React技术栈

第1章 初入React世界

React特点

  • 专注视图层:React不是完整的MVC/MVVM框架,它提供清晰、简介的View视图层解决方案
  • Virtual dom:更高效的渲染方式
  • 函数式编程:把过去不断重复构造UI的过程抽象成了组件

虚拟元素

  • React通过创建与更新虚拟元素来管理整个Virtual DOM
  • 虚拟元素是真实元素的对应,它的构建和更新都是在内存中完成的,并不会真正渲染到DOM中
  • 虚拟元素类别
    • DOM元素:HTML标签首字母小写
    • 组件元素:组件元素首字母大写

使用JSX的优势

  • 类XML语法 ,容易接受
  • 增强JS语义:提供更加语义化且易懂的标签(将传统的HTML标签封装成React组件),我们就可以像使用HTML标签一样使用这个组件。
  • 结构清晰:JSX 让小组件更加简单、明了、直观。在有上百个组件及更深层标签树的大项目中,这种好处会成倍地放大。在函数作用域内,使用 jsx 语法的版本与使用原生 JavaScript 相比,其标签的意图变得更加直观,可读性也更高。
  • 抽象程度高:对于使用 jsx 的人来说,从 React0.11升级到 React0.12是无痛的——不需要修改任何代码。虽然不是灵丹妙药,但是 jsx 提供的抽象能力确实能够减少代码在项目开发过程中的改动。
  • 关注点分离:将HTML 标签以及生成这些标签的代码内在地紧密联系在一起。在 React 内,你不需要把整个程序甚至单个组件的关注点分离成视图和模板文件。相反,React 鼓励你为每一个关注点创造一个独立的组件,并把所有的逻辑和标签封装在其中。

链接:https://www.cnblogs.com/clearyang/p/6899639.html

React组件的构建方式

  1. React.createClass
  2. ES6 classes
  3. 无状态函数:无状态组件不像上述两种方法在调用时会创建新实例,它创建时始终保持了一个实例,避免了不必要的检查和内存分配,做到了内部优化。

React数据流

  • setState是一个异步方法
  • React组件设计
    • 智能组件:组件内部的交互欣慰,被选择后通过回调函数返回具体选择的索引
    • 木偶组件:组件外部在传入具体的索引,组件就像木偶一样被操控着​

《你不知道的JS(中)》读书笔记4

发表于 2018-06-25 | 分类于 读书笔记 , 你不知道的JS

第1章 类型

  • 检测null值类型

    1
    2
    var a=null;
    (!a && typeof a==="object");
  • 变量没有类型,只有值才有类型。变量可以随时持有任何类型的值。

  • 在对变量执行typeof操作时,得到的结果并不是该变量的类型,而是该变量持有的值的类型

  • ReferenceError:b is not defined是指变量undeclared

  • 应对多个脚本文件在共享命名空间中加载变量的情况,有两种方法:

    • 使用typeof Undeclared安全防范机制
    • 检查全局变量,访问不存在的对象属性不会产生ReferenceError错误

第2章 值

数组

  • 使用delete运算符可以将单元从数组中删除,但是单元删除后,数组的length不会发生变化

  • 数组也是对象,可以包含字符串键值和属性

    1
    2
    3
    4
    5
    6
    var a=[];
    a[0]=1;
    a["footer"]=2;
    a.length; //1
    a["footer"]; //2
    a.footer; //2
  • 如果字符串键值能够被强制类型转换成十进制数字的话,它就会被当做数字索引来处理

    1
    2
    3
    var a=[];
    a["13"]=42;
    a.length; //14
  • 将arguments对象(类数组)将函数的参数当做列表来访问

    1
    2
    3
    var arr=Array.prototype.slice.call(arguments);
    // 以上在ES6中已经废除,在ES6中使用如下形式
    var arr=Array.from(arguments);

字符串

  • 字符串不可变是指字符串的成员函数不会改变其原始值,而是创建并返回一个新的字符串,而数组的承运函数都是在其原始值上进行操作。

  • 虽然字符串没有这些函数,但是可以通过“借用”数组的非变更方法来处理字符串

  • 字符串反转,split会把字符串转成字符数组,join会把字符数组变成字符串

    1
    2
    var a="foo";
    var c=a.split("").reverse().join(""); // "oof"

数字

  • JavaScript使用64位二进制存储数字

  • toExponential():科学计数法

  • tofixed():指定小数部分的显示位数

  • toPrecision():指定有效数位的显示位数

  • 42.toFixed(3)无效,但是42..toFixed(3)有效,因为.运算符会被优先识别为数字常量的一部分,然后才是对象属性访问运算符

  • 0.1+0.2===0.3//false处理误差范围值,通常称为“机器精度”

    1
    2
    3
    function numbersCloseEnoughtToEqual(n1,n2){
    return Math.abs(n1-n2)<Number.EPSILON
    }
  • 最大浮点数 Number.MAX_VALUE

  • 最小浮点数 Number.MIN_VALUE

  • 安全呈现的最大整数 Number.MAX_SAFE_INTEGER

  • 安全呈现的最小整数 Number.MIN_SAFE_INTEGER

  • void ___返回结果是undefined

特殊的数字

NaN

  • 不是数字的数字
  • 唯一的非自反值

  • ES6开始使用Number.isNaN(…)

  • ES6之前使用如下代码

    1
    2
    3
    4
    5
    6
    7
    8
    if(!Number.isNaN){
    Number.isNaN=function(n){
    return(
    typeof n==="number"&&
    window.isNaN(n)
    )
    }
    }

无穷数

  • 可以从有穷走向无穷,但无法从无穷回到有穷
  • Infinity/Infinity结果是NaN

负零

  • 区分0和-0

    1
    2
    3
    4
    function isNegZero(n){
    n=Number(n);
    return (n===0)&&(1/n===-Infinity);
    }
  • ES6中Object.is(..)处理特殊值的相等比较

值和引用

  • 引用无法改变另外一个引用的指向

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function foo(x){
    x.push(4);
    x; //[1,2,3,4]
    x=[4,5,6];
    x.push[7];
    x; //[4,5,6,7]
    }
    var a=[1,2,3];
    foo(a);
    a; // [1,2,3,4]

    函数传递a的时候,将引用a的复本赋值给x,但a仍然指向[1,2,3]。x=[4,5,6]并不影响a的指向

  • 有两种方式:值复制、引用复制。基本数据类型是值复制,引用数据类型是引用复制。如果要让基本数据类型使用引用复制,可以写在一个对象中,相对的,也可以将引用类型进行一次浅复制。

  • 标量基本类型值是不可更改的

    1
    2
    3
    4
    5
    6
    7
    function foo(x){
    x=x+1;
    }
    var a=2;
    var b=new Number(a);
    foo(b);
    console.log(b); //Number {2}

    ​

《你不知道的JS(上)》读书笔记3

发表于 2018-06-22 | 分类于 读书笔记 , 你不知道的JS

第4章 混合对象“类”

  • 多态在继承链的不同层次中一个方法名可以被多次定义,当调用方法时会自动选择合适的定义。
  • 子类对继承到的一个方法进行“重写”,不会影响父类中的方法,这两个方法互不影响,因此才能使用相对多台引用访问父类中的方法。

混入

  • 混入:模拟类的复制行为

  • 显式混入:手动实现复制功能,但是对象只能复制引用,无法复制被引用的对象或者函数本身。

    1
    2
    3
    4
    5
    6
    7
    8
    function mixin(sourceObj,targetObj){
    for(var key in sourceObj){
    if(!key in targetObj){
    targetObj[key]=sourceObj[key];
    }
    }
    return targetObj;
    }
  • 寄生继承:显式混入模式的一种变体,既是显式的又是隐式的。

  • 隐式继承:类似显式混入伪多态(OtherObj.methodName.call(this, ...))

第5章 原型

  • 使用for..in遍历对象时原理和查找[[Prototype]]链类似,任何可以通过原型链访问到(并且enumerable)的属性都会被枚举。

属性设置和屏蔽

myObject.foo="bar"

  1. myObject对象中有foo,原型链上没有:会修改已有属性值。

  2. myObject对象中有foo,原型链上也有foo:屏蔽,修改myObject中的foo。

  3. myObject没有foo,原型链上有foo:三种情况

    3.1 原型链上有foo,且没有被标记成只读。会在myObject中添加一个名为foo的新属性。

    3.2 原型链上有foo,但是是只读的。赋值语句会被忽略,严格模式下会抛出错误。

    3.3 圆形脸上有foo,并且是一个setter。赋值语句被忽略,并调用这个setter。

  4. myObject没有foo,原型链上也没有:在myObject上添加foo。

“类”函数

1
2
3
4
5
6
7
8
function Foo(){
...
}
var a=new Foo();
Objecr.getPrototypeOf(a) === Foo.prototype; //true
Foo.prototype.constructor === Foo; //true
a.constructor === Foo;
//true 实际a本身并没有.constructor属性,会委托[[Prototype]]链上的Foo.prototype
  • 继承:继承意味着复制操作,但是JS并不会复制对象属性。JS会在两个对象之间创建一个关联,这样一个对象就可以通过委托访问另一个对象的属性和函数。
  • 差异继承:在描述对象行为时,使用其不同于普遍描述的特质。

继承

  • Object.create(新创建对象的原型对象)会创建一个拥有null[[Prototype]]连接的新对象,并把新对象内部的[[Prototype]]对象并把它关联到你指定的对象。
  • a=Object.create(b)b是新创建对象a的[[prototype]]
  • Bar.prototype=Object.create(Foo.prototype)创建一个关联到Bar.prototype的新对象。
    • LHS先查找Bar.prorotype,找到后对其重新赋值
    • 赋予的值是Foo.prototype
  • 在ES6后,可以使用新方法:Object.setPrototyprOf(Bar.prototype,Foo.prototype)

检查”类”关系

  • 内省(反射):检查一个实例的继承祖先
  • 对象和函数之间的关系:instanceof
  • 两个对象之间的关系:isPrototyprOf
  • 直接获取一个对象的[Protoype]链:Object.getPrototypeOf(a)或者a._proto

第6章 行为委托

  • 类和委托的区别:
    • 在[[prototype]]委托中,最好把数据成员保存在委托者而不是委托目标上
    • 在类中,会利用多态、重写的优势。但是在委托中,要尽量避免这么做,否则就需要笨拙脆弱的语法: 隐式继承:(OtherObj.methodName.call(this, ...))
    • 调用位置触发this的隐式绑定规则。
  • 委托行为意味着某些对象在找不到属性或者方法引用时会把这个请求委托给另一个对象

比较思维模型

  • 面向对象(原型)风格代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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();

    js-1

  • 对象关联风格代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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();

  • 使用构造函数,需要在同一步骤中实现构造和初始化var b1=new Bar(),但是对象关联风格代码分开两步,b1=Object.create(Bar);b1.init("b1");可以根据需要在创建后对象后,再在合适的位置初始化,更灵活,更具有优势。

  • 行为委托模式中,Bar和Foo都是对象,它们之间是兄弟关系,并不是父类和子类的关系。

更好的语法

  • ES6中可以在任意兑现改的字面形式中使用简洁方法声明:

    1
    2
    3
    4
    var LoginController={
    errors:[],
    bar(){}
    }
  • 存在的问题是:在对象创建方法的语法,其实是一个匿名函数表达式并赋值给bar属性,因此不具备自我引用的词法标识符。

内省

  • 常见但脆弱的内省模式——鸭子类型:if(a1.something){a1.something()}
  • ES6中的Promise就是典型的“鸭子类型”

ES6中的class

ES6的class解决了哪些问题

  • 不再使用杂乱的.prototype
  • extends直接继承,不需要Object.create()
  • 可以通过super()实现多态
  • class字面语法不能声明属性,只能声明方法,避免获取其他地方的属性。
  • 可以通过extends扩展对象子类型,甚至是内置的对象子类型。

class陷阱

  • 若修改父类的方法,子类的所有实例都会受到影响。
  • 无法定义类成员属性,如果要跟踪实例之间共享状态,必须要使用.prototype语法
  • class中会有意外屏蔽的问题
  • super不一定会绑定到合适的对象,需要使用toMethod(…)手动绑定

《你不知道的JS(上)》读书笔记2

发表于 2018-06-20 | 分类于 读书笔记 , 你不知道的JS

第1章 关于this

  • 为什么要使用this
    • 显示传递上下文对象会让代码变得越来越混乱,this提供了一种更优雅的方式隐式地“传递”一个对象引用。
  • this并不指向函数本身
  • this在任何情况下都不指向函数的词法作用域
  • this是在函数被调用时发生的绑定,是函数运行时绑定,不是编写时绑定。

第2章 this全面解析

  • 调用栈:为了达到当前执行位置所调用的所有函数
  • 调用位置:函数在代码中被调用的位置(而不是声明的位置)

绑定规则

默认绑定

  • 不带任何修饰的函数引用进行调用的函数,只能使用默认绑定,无法应用其他规则
  • 只有运行在非严格模式下,this的默认绑定才能绑定到全局对象

隐式绑定

  • 函数引用拥有上下文对象时,通常是被某个对象拥有或者包含,隐式绑定规则会把函数调用中的this绑定到这个上下文对象
  • 对象属性引用链中只有最顶层或者说最后一层会影响调用位置。
  • 函数别名、参数传递、调用回调函数的函数都可能会丢失this的隐式绑定

显式绑定

  • call(...)和apply(...)方法指定this的绑定对象
  • 硬绑定:创建一个函数a,在其内部将另一个函数b的this绑定,之后无论如何调用a,都会显式强制绑定。
  • 硬绑定的典型应用场景是创建一个包裹函数,传入所有的参数并返回接收到的所有值。

new绑定

  • 构造函数只是被new操作符调用的普通函数而已
  • 实际上并不存在所谓的构造函数,只是对于函数的构造调用
  • new调用函数时,执行的操作:
    • 创建(或者说构造)一个全新的对象
    • 这个新对象会被执行[[原型]]链接
    • 这个新对象会绑定到函数调用的this
    • 如果函数没有返回其他对象,那么new的函数调用会自动返回这个新对象

优先级

  • new 绑定>显示绑定>隐式绑定>默认绑定
  • 之所以要在new中使用硬绑定函数,主要目的是预先设置函数的一些参数,这样在使用new进行初始化的时候就可以值传入其余的参数。
  • 部分应用(柯里化的一种):bind(..)的功能之一是可以把除了第一个参数(第一个参数用于绑定this)之外的其他参数都传给下层的函数。

绑定例外

  • DMZ对象——它是一个空的非委托的对象
  • 创建一个空对象最简单的方法是Object.create(null)。{}会创建Object.prototype,但是Object.create不会创建,所以比{}更空。
  • 间接引用:创建一个函数的“间接引用”,在这种情况下,调用这个函数会应用默认绑定规则。

软绑定

  • 硬绑定会大大降低函数的灵活性,使用应绑定之后就无法使用隐式绑定或者显式绑定来修改this。

  • 软绑定,会对指定的函数进行封装,首先检查调用时的this,如果this绑定到全局对象或者undefined,那就把指定的默认对象obj绑定到this,否则不会修改this。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    if(!Function.prototype.softBind){
    Function.prototype.softBind=function(obj){
    var fn=this;
    var curried=[].slice.call(arguments,1);
    var bound=function(){
    return fn.apply(
    (!this||this===(window||global))?obj:this.curried.concat.apply(curried,arguments)
    );
    }
    bound.prototype=Object.create(fn.prototype);
    return bound;
    }
    }

This词法

  • 箭头函数不适用this的四种标准规则,而是根据外层(函数或者全局)作用域来决定this,且绑定后无法被修改。

第3章 对象

  • 在必要时语言会自动把字符串字面量转换成一个String对象
  • object.a:属性访问
  • object[“a”]:键访问
  • 在对象中,属性名永远都是字符串
  • 数组也可以添加命名属性,数组的length值并不会发生变化
  • 访问对象属性的时候,返回值为undefined。这个值可能原属性存储的undefined,也可能是因为属性不存在所以返回undefined。解决方法:
    • in操作符检查属性是否在对象及其[[prototype]]原型链中。in操作符检查容器内某个属性名是否存在。
    • hasOwnProperty(..)只会检查属性是否在对象中,不会检查[[prototype]]原型链中。
  • Object.keys(…)会返回一个数组,包含所有可枚举属性
  • Object.getOwnPropertyNames(…)会返回一个数组,包含所有属性,无论它们是否可枚举。

遍历

  • for…in循环可以用来遍历对象的可枚举属性列表(包括[[Prototype]]链)
  • for…of ES6新增遍历数组的语法,直接遍历值而不是数组下标。
  • Symbol.iterator获取对象的@@iterator内部属性。
  • @@iterator本身不是一个迭代器对象,而是一个返回迭代器对象的函数。
  • 数组有内置的@@iterator,但是普通对象没有。可以通过下面的代码给想遍历的对象定义@@iterator.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Object.defineProperty(myObject,Symbol.iterator,{
enumerable:false,
writable:false,
configurable:true,
value:function(){
var o=this;
var idx=0;
var ks=Object.keys(o);
return {
next:function(){
return{
value:o[ks[idx++]],
done:(idx>ks.length)
}
}
}
}
})

《你不知道的JS(上)》读书笔记1

发表于 2018-06-14 | 分类于 读书笔记 , 你不知道的JS

第1章 作用域是什么

  • JS不是提前进行编译的,大部分情况下编译发生在代码执行前的几微秒
  • 编译的三个步骤:
    • 分词/语法分析:将由字符组成的字符串分解成有意义的代码块(词语单元)
    • 解析/语法分析:将词法单元流(数组)转换成一个由元素逐级嵌套所组成的代表了程序语法结构的树(抽象语法树AST)
    • 代码生成:将AST转换为可执行代码的过程
  • 引擎:负责JS编译及执行过程
  • 编译器:语法分析及代码生成
  • 作用域:收集并维护由所有声明的标识符(变量)组成的一系列查询
  • 变量的赋值操作会执行两个动作,首先编译器会在当前作用域中声明一个变量(如果之前没有声明过),然后在运行时引擎会在作用域中查找该变量,如果能够找到就会对它赋值。
  • LHS:对目标进行赋值
  • RHS:取到变量的值
  • ReferenceError:RHS查询在所有嵌套的作用域中遍寻不到需要的变量时抛出的异常
  • TypeError:对变量的值进行不合理的操作抛出的异常

第2章 词法作用域

  • 作用域有两种主要的工作模型:词法作用域、动态作用域
  • 词法作用域:定义在词法阶段的作用域。词法作用域是由你在写代码时将变量和块作用域写在哪里来决定的。
  • 遮蔽效应:作用域查找会在找到第一个匹配的标识符时停止
  • 词法作用域查找只会查找一级标识符。

欺骗词法

  • 词法作用域由函数声明时所处的位置决定,在运行时欺骗词法作用域的两种方法:
    • eval(..):动态创建的代码,将代码欺骗和假装成书写时代码就在那里,来实现修改词法作用域环境
    • with(…):重复引用同一个对象中的对个属性的快捷方式。with块可以将对象处理为词法作用域,但是这个块内部正常的var声明并不会被限制在这个块的作用域中,而是会被添加到with所处的函数作用域中。

性能

  • JS引擎会在编译阶段进行数项性能优化,有些优化依赖于能够根据代码的词法进行静态分析,并预先确定所有变量和函数的定义位置
  • 在代码中出现eval()和with()只能简单地假设关于标识符位置的判断都是无效的,故引擎无法在编译时对作用域查找进行优化

第3章 函数作用域和块作用域

  • 最小授权或最小暴露原则:在软件设计中,应该最小限度地暴露必要内容,而将其他内容都“隐藏”起来。

规避冲突

  • “隐藏”作用域中的变量和函数所带来的另一个好处,是可以变同名标识符之间的冲突。
  • 两种方法:
    • 设置对象为一个全局命名空间
    • 通过依赖管理器的机制将库的标识符显式地导入另外一个特定的作用域中

函数作用域

  • function foo(){..}是函数声明,被绑定在所在作用域中,直接通过foo()来调用它
  • (function foo(){..})是函数表达式,被绑定在函数表达式自身的函数中,在…代表的位置访问foo。
  • 函数表达式可以是匿名的,函数声明不可以省略函数名
  • IIFE(立即执行函数表达式):(funtion(){...})() 或(funtion(){...}()) 函数表达式加上括号
  • IIFE:把他们当做函数调用并传递参数进去。
  • IIFE会通过声明并立即执行一个函数来创建作用域

块作用域

  • 创建块作用域的方法
    • with
    • try/catch
    • let和const声明

第4章 提升

  • 先有声明后又赋值。只有生命本身会被提升,赋值或其他运行逻辑会留在原地。
  • 函数声明会被提升,但是函数表达式并不会被提升
  • 在多个“重复”声明的代码中,函数会首先被提升,然后才是变量。
  • 重复声明的代码中,后面出现的函数声明可以覆盖前面的函数声明

第5章 作用域闭包

  • 当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。
  • 只要使用了回调函数,实际上就是在使用闭包
  • IIFE并不是在它本身的词法作用域以外执行的,是在定义时所在的作用域中执行。

模块

  • 最常见的实现模块模式的方法是模块暴露
  • 模块模式具备两个必要条件:
    • 必须有外部的封闭函数,该函数必须至少被调用一次
    • 封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问或者修改私有的状态。
  • 基于函数的模块并不是一个能够被稳定识别的模式(编译器无法识别),它们的API语义只有在运行时才会被考虑,因此可以在运行时修改一个模块的API。
  • ES6的模块API更加稳定,可以再编译器检查对导入模块的API成员的引用是否真实存在。

动态作用域

  • 词法作用域是在写代码或者说定义时确定的,而动态作用域是在运行时确定的。

  • JavaScript并不具有动态作用域,只有词法作用域。

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function foo(){
    console.log(a);
    }
    function bar(){
    var a=3;
    foo();
    }
    var a=2;
    foo(); //2
    bar(); //2

This词法

  • 可能会丢失this绑定的问题,最常用的解决方案是 var self=this
  • 箭头函数放弃了所有普通this绑定的规则,是用当前的词法作用域覆盖了this本来的值
  • 箭头函数不够理想的原因:
    • 箭头函数混淆了this绑定规则和词法作用域规则
    • 箭头函数是匿名的,而非具名的

《HTTP权威指南》读书笔记1

发表于 2018-06-12 | 分类于 读书笔记 , HTTP权威指南

第1章 HTTP概述

  • HTTP使用的是可靠的数据传输协议
  • 客户端向服务器发送HTTP请求,服务器会在HTTP相应中回送所请求的数据
  • 所有能够提供Web内容的东西都是Web资源
  • Web服务器会为所有HTTP对象数据附加一个MIME类型的数据格式标签
  • URI:服务器资源名称被称为统一资源标识符URI。URI有两种形式:URL和URN。
  • URL:统一资源定位符(URL)是资源标识符最常见的形式。URL 描述了一台特定服务器上某资源的特定位置。
  • URN:URI 的第二种形式就是统一资源名(URN)。URN 是作为特定内容的唯一名称使用
    的,与目前的资源所在地无关。
  • HTTP 事务由一条(从客户端发往服务器的)请求命令和一个(从服务器发回客户端的)响应结果组成。
  • 从 Web 客户端发往 Web 服务器的 HTTP 报文称为请求报文(request message)。从服务器发往客户端的报文称为响应报文(response message)
  • 浏览器通过 HTTP 显示HTML资源步骤:
    • 浏览器从 URL 中解析出服务器的主机名;
    • 浏览器将服务器的主机名转换成服务器的 IP 地址;
    • 浏览器将端口号(如果有的话)从 URL 中解析出来;
    • 浏览器建立一条与 Web 服务器的 TCP 连接;
    • 浏览器向服务器发送一条 HTTP 请求报文;
    • 服务器向浏览器回送一条 HTTP 响应报文;
      关闭连接,浏览器显示文档。
  • 代理:接收所有客户端HTTP请求,并将这些请求转发给服务器
  • 缓存:将经过代理传送的常用文档复制保存起来,下一个请求统一文档的客户端可以享受缓存的私有副本。
  • 网关:特殊的服务器,将HTTP流量转换成其他协议。
  • 隧道:建立起来以后,就会在两条链接之间对原始数据进行盲转发的HTTP应用程序。常见用途是通过HTTP链接承载加密的安全套接字层SSL。
  • Agent代理:代表用户发起HTTP请求的客户端程序。所有发布Web请求的应用程序都是HTTP Agent代理。

第2章 URL与资源

  • URL分为以下三个部分:
    • URL方案,告知web客户端怎样访问资源
    • 服务器位置,告知客户端资源位于何处
    • 资源路径,请求的是服务器上那个特定的本地资源。
  • 片段#:引用部分资源或资源的一个片段

第3章 HTTP报文

  • HTTP报文是在HTTP应用之间发送的数据块。这些数据块以一些文本形式的元信息开头。

  • 报文的组成部分:

    • 起始行,对报文进行描述
    • 首部块,包含属性
    • 主体,可选的、包含数据的部分

    ​

《深入浅出ES6》读书笔记1

发表于 2018-06-09 | 分类于 读书笔记 , 深入浅出ES6

第八章 迭代器与生成器

  • 生成器函数会在每个yield语句后停止执行

  • 可以使用函数表达式创建一个生成器,只要再function关键字和圆括号之间使用一个星号(*)即可

    1
    2
    3
    4
    5
    6
    7
    let createIterator=function *(items){
    for(let i=0;i<items.length;i++){
    yield items[i];
    }
    };
    let iterator=createIterator([1,2,3]);
    console.log(iterator.next());
  • Symbol.iterator():访问对象上的默认迭代器,也可以用于创建自定义的可迭代对象

  • entries():返回一个包含键值对的迭代器

  • values():返回一个包含集合中的值的迭代器

  • keys():返回一个包含集合中键的迭代器

  • values是数组与Set的默认迭代器,entries()是Map的默认迭代器

异步任务运行

  • yield能停止运行,并在重新开始运行前等待next()方法被调用,可以在没有回调函数的情况下实现异步调用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    function run(taskDef){
    // 根据传入的生成器,创建迭代器
    let task=tsakDef();
    // 保存执行迭代器之后的值
    let result=task.next();
    function step(){
    if(!result.done){
    // 每次调用next()就把返回的结果保存在result上
    result=task.next();
    step();
    }
    }
    step();
    }
    // 生成器
    run(function*()){
    console.log(1);
    yield;
    console.log(2);
    yield;
    console.log(3);
    }

第九章 JS的类

:warning: ES5不存在类,会创建一个构造器,然后将方法指派到该构造器的原型上。ES6中新增了类。

  • 类声明,外部绑定和内部绑定有着相同的名称
  • 类表达式,外部绑定和内部绑定可以有不同的名称
  • 能被当做值来使用的就统称为一级公民,意味着它能作为参数传给函数、能作为函数返回值、能用来给变量赋值
  • Symbol.species被用于定义一个能返回函数的静态访问器属性。
  • 可以使用new.target创建一个抽象基类(一种不能被实例化的类)

第十章 增强的数组功能

:warning:ES5中创建数组有两种方式:Array构造器和数组字面量写法,在书写时需要将数组项别分别列出,且”类数组对象”转换为数组并不自由。ES6新增Array.of()和Array.from()方法

  • Array.of():创建一个包含所有传入参数的数组
  • Array.form():传入可迭代对象或者类数组对象,能返回一个数组
  • fill(特定数值,起始位置,结束未知):使用特定值填充数组中的一个或多个元素
  • copyWithin(填充起始位置,复制起始位置,复制结束位置):在数组内部复制自身元素
  • new ArrayBuffer():创建内存中包含一定数量字节的区域
  • new DataView():创建数组缓冲区的通用视图
  • ES6的类型化数组实际上也是针对数组缓冲区的特定类型视图,截图使用这些数组对象来处理特定的数据类型,而不必使用通用的DataView对象。
  • 类型化数组与常规数组的比较:
    • 可以使用length属性获取类型化数组包含的元素数量,但是不能通过length属性修改数组的长度
    • 可以通过数值类型的索引值直接访问类型化数组的元素
    • 类型化数组的方法会进行额外的类型检查以确保安全,并且返回值会是某种类型化数组
    • 类型化数组不是从Array对象派生的,使用Array.isArray()检测会返回false
    • 常规数组可以被伸展或是收缩,然而类型数组会保持自身大小不变

第十一章 Promise与异步编程

:warning:ES5中会存在回调地狱问题,ES6提出Promise

  • Promise的catch()方法,等同于只传递拒绝处理函数给then()
  • 创建未决的Promise:新的Promise使用promise构造器创建,接受单个参数:一个被称为执行器的函数,包含初始化Promise的代码,该函数传递resolve()和reject()
  • 创建已决的Promise:
    • Promise.resolve()接受单个参数并会返回一个处于完成态的Promise
    • Promise.reject()
  • 所有的Promise都是Thenable,但并不是所有的Thenable都是Promise
  • 全局的Promise拒绝处理:当一个Promise被拒绝时若缺少拒绝处理函数,就会静默失败
  • Node.js和浏览器的拒绝处理:process对象上存在两个时间:unhandledRejection和rejectHandled
  • promise.all():接收单个可迭代对象作为参数,并返回一个Promise
  • promise.race():接收单个可迭代对象作为参数,并返回一个Promise。但是一旦来源Promise中有一个被解决,所返回的Promise就会立刻被解决。

第十二章 代理与反射接口

:warning:ES5的特定对象(例如数组)会显示出一些非常规的行为,代理的出现改变了这种情况,代理允许你为一些JS底层操作自定义常规行为。

  • 反射接口允许开发者为每个代理陷阱实现默认的行为
  • 每个代理陷阱在Reflect对象上都有一个同名的对应方法。

第十三章 用模块封装代码

  • 模块是使用不同方式加载的JS文件
  • ES6的import语句为变量、函数与类创建了只读绑定,外部模块导入的变量会始终反应导入变量的变化
  • default指默认导出,导出内容并不需要有名称,代表该模块本身。
  • 导入默认值不需要加花括号,导入非默认值需要加花括号。默认名称要位于非默认名称之前。
12…6
Xyy

Xyy

I want to get ahead in a step-by-step

59 日志
16 分类
8 标签
© 2018 Xyy
由 Hexo 强力驱动
|
主题 — NexT.Gemini v5.1.4