权威指南读书笔记 第一部分

关于省略分号的换行

javascript在解析时,会把省略分号的行与下一行一起解析,当不构成语法错误时回合为一行,因此类库的保守写法,会在已开始前添加分号,以防出现上述问题。

关于分数的精度问题

javascript可以精确地表示分数1/2、1/8等,通常所用的1/10、1/100不能被精确地表示出来。

    var x=.3-.2;
    var y=.2-.1;
    x==y //false
    x==.1 //false
    y==.1 //true

解决方案,当进行相关计算可以换算成整数计算,最后使用科学计数法或单位换算进行结果的应用。

关于进制的问题

严格模式下禁止8禁止模式。ECMAscript不支持八进制的直接量。

多行字符串

JS中多遇到拼接字符串的问题,通常情况下会拼接一个超长字符串在一行,使文档可读性下降,可以使用换行符,使得一个字符串可以在多行写出。
在ECMAscript 3 中,字符串必须写在一行,但是在ESMAscript5 中字符串可以被拆成多行,每行一反斜线()结束,反斜线和行结束符都不算是字符串直接量的内容。如果希望再字符串直接量中另起一行,可以使用转义字符\n

    "on
    \line" 
    //输出为online

关于转义字符

反斜杠使我们避免使用常规方式解释单引号,当单引号不是用来标记字符串结尾时,他只是一个‘。
ESMAScript5,允许在一个多行字符串直接量里的每行结束处使用反斜杠。

You\'re right, it can\'t be a quote.

关于模式匹配

尽管RegExp并不是语言中的基本数据类型,但是他们依然具有直接量写法,可以直接在javascript程序中使用。在两条斜线之间的文本它构成了一个正则表达式直接量。第二条斜线之后也可以跟随一个或多个字母,用来修饰匹配模式的含义

    var text = "testing:1,2,3";
    var pattern=/\d+/g
    partten.test(text) // 匹配成功
    text.search(pattern) //9 首次匹配成功的位置
    text.match(pattern) //["1","2","3"]所有匹配组成的数组
    text.replace(pattern,"#"); //"testing:#,#,#"
    text.split(/\D+/);//["","1","2","3"]用非数字字符截取字符串

关于全局对象

全局属性: undefined infinity NaN
全局函数: isNaN() parseInt() eval()
构造函数 : Date() RegExp() String() Object() Array()
全局对象:Math JSON

关于包装对象

对于string、bollen、num为什么会有属性呢,只要应用了字符串的属性,javascript就会将字符串值通过调用new String(s)的方式注册能换成为对象,这个对象继承了字符串的方法。并被用来处理属性的引用,一旦属性引用结束,这个新创建的对象就会销毁。
  当读取字符串、数字、和布尔值的属性值的时候,表现的想对象一样,但是如果你试图给属性赋值,则会忽略这个操作;修改只是发生在临时对象上,而这个临时对象并没有被保留下来。

  • 存取字符串和数字布尔值的属性时创建的临时对象称为包装对象。
  • 字符串数字和布尔值的属性值是只读的,并且不能定义新属性。

关于对象的转换

对象到字符串

  • 调用toString()方法,把返回值进行字符串的隐式转换。
  • 没有toString()方法,调用valueOf(),并隐式转换返回值。
  • 否则抛出类型异常

对象到数字

  • 对象具有valueOf()方法,把返回值进行数字的隐性转换。
  • 没有valueOf()方法,调用toString()方法,把返回值进行数字的隐形转换
  • 否则抛出类型异常

空数组返回0的原因:
数组继承了默认的valueOf()方法,这个方法返回一个对象而不是一个原始值,因此数组到数字的转换则调用toString方法。空数组转换成为空字符串,空字符串转换为数字0.

关于作为属性的变量

    var trunevar = 1; //声明一个不可删除的全局变量
    fakevar = 2;//创建全局对象的一个可删除的属性
    this.fakevar2 = 3;
    delete trunevar //false
    delete fakevar //true
    delete this.fakevar2 //true

关于运算符

运算要求操作的数是整数,这些整数表示32位整型

按位非:
~ox0F = oxFFFFFFF0  表示为-16

左移和右移的位数是0~31之间的一个整数

带符号右移:
右边溢出将忽略,左边按照原有操作数符号来补位,补成一致的。

无符号右移:
最高位总是由0填补
-1>>>4=ox0FFFFFFF

关于相等和严格相等

严格相等:

  • 明确类型不进行类型转换
  • null ===null //false
  • undefined ===undefined //false
  • NaN === NaN //false

相等:

  • null ==undefined //true
  • '1' == true //true

关于逻辑非

恒等式
!(p&&q) === !p||!q
!(p||q) === !p&&!q

关于eval()

    var geval = eval;
    var x ='global',y='global';
    function f(){
        var x='local';
        eval("x+='change';");//改变局部变量
        return x;
    }
    function g(){
        var y='local';
        geval("y+='change';");//改变全局变量
        return y;
    }
    console.log(f(),x);   //"local changeglobal"
    console.log(g(),y);   //"local globalchange"

关于 typeof

数据类型 计算值
NaN number
函数 function
宿主对象 由编译器各自实现的字符串

本地对象、内置对象和 宿主对象

1、本地对象

ECMA-262 把本地对象(native object)定义为“独立于宿主环境的 ECMAScript 实现提供的对象”。

再来看一下,“本地对象”包含哪些内容:

Object、Function、Array、String、Boolean、Number、Date、RegExp、Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError

由此可以看出,简单来说,本地对象就是 ECMA-262 定义的类(引用类型)。

2、内置对象

ECMA-262 把内置对象(built-in object)定义为“由 ECMAScript 实现提供的、独立于宿主环境的所有对象,在 ECMAScript 程序开始执行时出现”。这意味着开发者不必明确实例化内置对象,它已被实例化了。

同样是“独立于宿主环境”。根据定义我们似乎很难分清“内置对象”与“本地对象”的区别。而ECMA-262 只定义了两个内置对象,即 Global 和 Math (它们也是本地对象,根据定义,每个内置对象都是本地对象)。

如此就可以理解了。内置对象是本地对象的一种。而其包含的两种对象中,Math对象我们经常用到,可这个Global对象是啥东西呢?

Global 对象是ECMAScript中最特别的对象,因为实际上它根本不存在,但大家要清楚,在ECMAScript中,不存在独立的函数,所有函数都必须是某个 对象的方法。类似于isNaN()、parseInt()和parseFloat()方法等,看起来都是函数,而实际上,它们都是Global对象的方 法。而且Global对象的方法还不止这些。有关Global对象的具体方法和属性,感兴趣的同学可以看一下这里:JavaScript 全局对象参考手册

3、 宿主对象

由ECMAScript实现的宿主环境提供的对象,可以理解为:浏览器提供的对象。所有的BOM和DOM都是宿主对象。

关于void 运算符

出现在操作数之前,操作数可以是任意类型。void会忽略操作数的值,因此在操作数据有副作用的时候使用void来让程序更具有语意。

运算符用于客户端的URL——javascript:URL中,在URL中可以写带有副作用的表达式,而void则让浏览器不必显示这个表达式的计算结果

打开一个新的窗口

关于switch

  • 查找case中的表达式和expression的值全等于(===)进行匹配,表达式和case不会做模式的转换。
  • 如果匹配不成功的话会执行default,如果没有default则会跳过所有的代码块。
  • case只表明了起点,并没有标明终点,因此需要使用break使解释器跳出switch语句或循环语句,否则程序将执行下一个case。

关于循环

循环变量多是数字,下例为使用for循环来遍历链表数据结构,并返回链表中的最后一个对象。

function tail(o) {
    for( ; o.next; o=o.next)
    return o
}

关于for in 循环

for(variable in object)
    statement
  • 解释器先计算object表达式,如果表达式为null或者undefined,javascript解释器回调过循环并执行后续代码。
  • 每次循环之前,都会计算variable表达式的值,并将属性名复制给他。

break&containue

break labelname //适用于嵌套多层的循环
containue labelname //适用于嵌套的循环中,用以跳出多层次嵌套的循环体逻辑

当break和标签一起使用的时候,程序将跳转到这个标签所表示的语句块的结束,或者直接终止这个闭合语句块的执行。

try/catch/finally

try{
        //通常来讲,这里的代码不会出现问题
        //有时抛出一个异常,要么由throw语句直接抛出异常
        //要么通过调用一个方法间接抛出异常
}catch (e){
        //当且仅当try语句抛出异常,才会执行的代码
        //这里可以通过局部变量e来获取error对象或者抛出的其他值引用
        //这里的代码可以给予某种原因处理这个异常
        //可以通过throw重新抛出异常
}finally{
        //不管try语句块是否抛出异常,这里的逻辑总是会执行的,终止try语句块的方式有
        //正常终止,执行完语句块的最后一条语句
        //通过break、containeu、return语句终止
        //抛出一个异常,异常被catch从句捕捉
        //抛出一个异常,异常未被捕获,继续向上传播
}

try中产生的异常没有catch进行处理,就会先执行finally之后,然后向上传播这个异常,知道找到能处理这个异常的catch从句。

关于严格模式

  • 禁止使用with
  • 所有变量需要声明
  • 调用函数中的this值为undefined,可以使用这个特性来检测是否为严格模式
var hasStrictMode = (function(){
    "use strict";
    return this === undefined
}());

关于检测对象

检测集合中成员的所属关系——判断某个属性是否存在于某个对象中。
可以通过in运算符、hasOwnPreperty()和propertyIsEnumerable()方法来完成这个工作,甚至仅通过属性查找也可以得到结果。
propertyIsEnumer(),当属性为自有的并且是可枚举的,才会返回true。

检测P是否是O的原型

`   p.isPrototypeOf(o);

关于序列化对象

对象序列化是指将对象转换为JSON格式的字符串。

    JSON.stringify() //把对象撞换为字符串
    JSON.parse() //把JSON格式字符串转换为对象

支持对象、数组、字符串、无穷大数字、bollen和null。
函数、RegExp、Error、undefined不能被序列化和还原。
NaN、Infinity的序列化结果是null。

关于toString()与toLocalString方法

在需要将对象转换为字符串的时候,javaScript会调用这个方法。比如说遇上+时。默认的该方法对于监测对象十分有用。

var s = {x:1,y:2}.toString(); //返回的是[Object,Object]

  • Object的toLocalString方法,只是调用了toString而已。
  • Date和Number对toLocalString方法做了定制,可以用它对数字日期和时间做本地化的转换
  • 数组元素的tolocalstring方法和toString方法很像,唯一的不同是每个数组元素会调用各自的tolocalstring完成字符串的转换,而不是点用各自的tostring方法。

关于数组的声明

var udf = [,,]  //数组的长度为2,原因是最后一个逗号可以省略也可以加上

关于稀疏数组

只包含从0开始的不连续索引的数组,即数组的长度大于数组元素的个数。

数组的长度

数组的长度是可配置的,通过改变数组的长度,删掉相关的索引值。使得数组相关的push和shift方法不能使用。

数组的相关操作

Slice
当出现负数的时候就是用数组的长度加上相应的负数,接收两个参数,第一个参数表示复制的起点,第二个表示终点。
splics
接收三个参数,操作位置,删除个数,插入元素,与concat不同,该方法接収元素而非数组
for-each

function foreach(a,f,t){
    try{ a.forEach(f,t)}
    catch(e) {
        if( e=== foreach.break) return;
        else throw e;
    }
}
foreach.break = new Error ("stopIteration");

reduce&reduceRight
接受两个参数分别是在每一项上调用的函数和作为归并基础值。
提供给内函数的四个参数分别是,此项,下一项,此项索引,以及数组。

检测数组

    Array.isArray() //ECMAScript5之前
    instanceof 不准
    //兼容3
    var isArray = function.isArray || fucntion(o){
        return typeof o ===="object" &&
        Object.prototype.toString.call(o) === "[object Array]";
    };

关于类数组

类数组的条件:

  • 自动更新length属性
  • 设置length为一个较小值将截断数组
  • 从Array.prototype中继承一些有用的方法
  • 其类属性为Array
    function isArrayLike(o) {
        if(o && 
            typeof o === 'object' &&
            isFinite(o.length) &&
            o.length >= 0 &&
            o.length === Math.floor(o.length) &&
            o.length < 4294967296++)
            return true
        else
            return false
    }

类数组的操作

    var a={'0':a, '1':b, '2':c, length:3};
    Array.prototype.join.call(a,"+")
    Array.prototype.map.call(a,function(x) {
        return x.toUpperCase();
    }) //["A","B","C"]

关于作为数组操作的字符串

字符串是不可变值的,只是可读的,所以使用push、sort、reverse、splice等方法是无效的,BUT不会报错。

关于数组的操作的分类

不改变数组值的 改变数组的值
forEach()等迭代方法 栈、队列方法
reduce、reduceRight归并方法 reverse
concat连接方法 splice
slice -----

关于函数的概念

  • 形参相当于函数中定义的变量,实参是调用函数时传入的参数。
  • javaScript中的函数是参数化的,函数定义的时候会包含 一个称为形参的标示符列表,这些参数在函数体重向局部变量一样工作。
  • 每次调用还会拥有另一个值,本次调用的上下文——this。
  • 如果通过访问对象方法调用函数,this指向该对象。
  • javascript 的函数嵌套在其他函数中定义,他们就可以访问他们被定义时所处的作用域中的任何变量,这意味这闭包。

关于函数定义

函数名称--name

function name (arguments){
    coments
}

表达式定义函数只适用于它作为一个大的表达式的一部分,比如在赋值和调用过程中定义函数。函数表达式定义函数之前无法调用函数,没有函数声明提升的过程。
如果一个函数定义表达式包含名称,函数的局部作用域将会包含一个绑定到函数对象的名称,函数名称将成为函数内部的一个局部变量。

var f = function fact(x){
    if(x <= 1)
        return 1;
    else
        return x*fact(x-1); //在函数外部将不能访问fact
}
fact(5); //fact is not defined

关于函数中的this

  • 当函数作为方法调用,this指向的是拥有该方法的对象。
  • 当函数作为函数调用,this指向的是全局对象,严格模式下为undefined。
  • 当函数是构造函数,this指向构造函数新创建的对象。
  • this不存在于作用域中,无法通过闭包的方式访问,如需访问可以把this的值付给你一个变量,使其存在于作用域中。
  • this无法被赋值。

构造函数的return

  • 构造函数一般没有return
  • 显示使用return 返回一个对象,这个对象会代替构造函数创建的对象复制给new操作符左侧的变量。
  • 如果return 为一个空语句或者返回值为原始值,那么这时将被忽略,同时把新生成的对象返回。

关于函数参数

  • 对于有需要的实参进行判断和赋值,对于可选参数使用/optional/来表示。
  • 对于不定实参函数,实参个数不能为零。
  • Argument.callee指向正在执行的函数,可以使用这个来进行函数内部的解耦。
  • 使用传入对象代替零散的参数,避免多参数函数调用时参数位置与参数对应的困难,提升语义性。
  • arguments.length (实际传入的实参个数)
  • arguments.callee.length(期望传入的实参个数)

关于用作命名空间的函数

利用作用域关系,定义一个函数用作临时的命名空间,在这个命名空间里定义的变量不会污染全局作用域。

(function(){
    //coding
}());

第一个左侧括号是必须的,否则解释器会试图将关键字function 解析为函数声明语句。

关于闭包

  • 同一个作用域链中定义的两个闭包,这两个闭包共享同样的私有变量。
function People(name){
    this.name = name;
    this.sayName = function(){name+=' hello';return name};
    this.sayNameAgain = function(){return name};
};
var lixinyue = new People('lixinyue');
lixinyue.sayName(); //lixinyue hello
lixinyue.sayNameAgain();//lixinyue hello
lixinyue.sayName();//lixinyue hello hello
lixinyue.sayNameAgain();//lixinyue hello hello
lixinyue.name //lixinyue
  • 关联到闭包的作用域都是活动的,记住这一点非常重要。嵌套函数不会将作用域内的私有成员复制一份,也不会对绑定的变量生成静态快照。

  • 闭包无法访问父级函数this和arguments,如果有这个需要在定义父级函数时就要将this和arguments赋给一个变量保存起来。

关于使用callapply

  • call和apply的第一个实参是要调用函数的母对象
  • 其后传入的参数数组可以是真实数组也可以是类数组

关于bind()

  • bind方法不仅仅是将函数绑定至一个对象,他还把传入的参数绑定到this。可以理解为把函数的形参在用函数内用绑定的值进行赋值。
  • 因此bind返回的函数length,是原函数对象的形参个数减去bind绑定的实参个数。
  • 当返回函数用于构造函数时,其所创建的对象从原始的未绑定的函数对象中继承prototype。

this 指针

  • 函数在被调用时,this指针是由调用者所决定的。
  • 直接调用时,this指针为Globel Object,浏览器端为window。
  • 方法调用时,this指针为方法所属对象。如a.b.c(),this指针为a.b。
  • 构造器调用时,解析器会创建一个Object,this指针为这个Object,默认这个Object会被返回

apply或call可以通过第一个参数指定调用时的this指针。

你可能感兴趣的:(权威指南读书笔记 第一部分)