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

1.JavaScript并不是在所有换行处填补分号:只有在缺少了分号就无法正确解析代码时,JavaScript才会填补分号,也就是说如果当前语句和随后的非空格字符不能当成一个整体来解析的话,JavaScript就在当前语句结束处填补分号。
注意:1.在涉及return,break,continue语句的场景中,如果这三个关键字后紧跟着换行,JavaScript则会在换行处填补分号。如
return 
true;
JavaScript会解析为:return ; true; 
也就是说,在return, break, continue 和随后的表达式之间不能有换行。如果添加了换行,程序则只有在极特殊的情况下才会报错,而且程序的调度非常不方便。
2."++"和"--"可以作为表达式的前缀,也可以作为表达式的后缀。如果将其用做后缀表达式,它和表达式应当在同一行。否则,行尾将填补分号,同时"++","--"将会作为下一行代码的前缀操作符并与之一起解析。如
x
++
y
这段代码将解析为"x;++y",而不是"x++;y"

2.JavaScript中除了数字,字符串,布尔值,null和undefined之外就是对象了。

   在JavaScript中,只有null和undefined是无法拥有方法的值。

3.JavaScript不区分整数值和浮点数值。
   最好不要使用以0为前缀的整型直接量(八进制)
   Javasc中的算术运算在溢出,下溢或被0整除时不会报错。当数字运算结果超过了JavaScript所能表示的数字上限(溢出),结果为一个特殊的无穷大的(infinity)值,以infinity表示,同样的,当负数的值超过了JavaScript所能表示的负数范围,结果为负无穷大,以-infinity表示,基于它们的加,减,乘除运算结果还是无穷大值,并保留它们的正负号。
   下溢是当运算结果无限接近于0并比JavaScript能表示的最小值还小的时候发生的一种情况。这时,JavaScript将返回0.当一个负数发生下溢时,JavaScript返回一个特殊的值"负零‘,这个值几乎和正常的零完全一样,JavaScript程序员很少用到负零。
    被零整除在JavaScript并不报错,它只是简单的返回无穷大(Infinity)或负无穷大(-Infinity)。但有一种例外,零除以零是没有意义的,结果是NaN,无穷大除以无穷大,给任意负数作开方运算或者算术运算符与不是数字或无法转换为数字的操作数一起使用时都返回Nan。


    JavaScript采用IEEE-754浮点数表示法,这是一种二进制表示法,可能精确表示1/2, 1/8, 1/1024, 但不能精确表示十分制分数1/10, 1/100。如
0.3 - 0.2 结果为 0.099 999 999 999 999 98;

4.下面JavaScript的值被转化为false:
undefined
null
0
-0
Nan
""//空字符串


typeof null结果为"object",可以将null认为一个特殊的对象值,含义为“非对象”
undefined是表示系统级的,出乎意料的或类似错误的值的空缺,而null是表示程序级的,正常的或在意料之中的值的空缺。如果你想将它们赋值给变量或属性,或将它们作为参数传入函数,最佳选择是使用null。



字符串也同样具有属性和方法:
var s = "hello world";
var word = s.substring(s.indexof(" ") + 1, s.length);
字符串既然不是对象,为什么它会有属性呢?只要引用了字符串s的属性,JavaScript就会将字符串值通过调用new String(s)的方式转换为对象,这个对象继承了字符串的方法,并被用来处理属性的引用。一旦属性引用结束,这个新创建的对象就会被销毁(其实在实现上并不一定创建或销毁这个临时对象,然而整个过程看起来是这样)。
数字和布尔值也可以通过Number()和Boolean()构造函数创建一个临时对象,这些方法的调用均是来自这个临时对象。null和undefined没有包装对象,访问它们的属性会造成一个类型错误。


每一段JavaScript代码(全局代码或函数)都有一个与之关联的作用域链(scope chain)。这个作用域链是一个对象列表或者链表,这组对象定义了这段代码“作用域中”的变量。当JavaScript需要查找变量x的值时(这个过程称做“变量解析”(variable resolution)),它会从链中的第一个对象开始查找,如果这个对象有一个名为x的属性,则会直接使用这个属性的值。如果第一个对象中不存在名为x的属性,JavaScript会继续查找链上的下一个对象。如果第二个对象依然没有名为x的属性,则会继续查找下一个对象,以此类推。如果作用域上没有一个对象含有属性x,那么就认为这段代码的作用域上不存在x,并最终抛出一个引用错误异常。
在JavaScript的最顶层代码中(也就是不包含在任何函数的定义内的代码),作用域由一个全局对象组成。在不包含嵌套的函数体中,作用域上有两个对象,第一个是定义函数参数和局部变量的对象,第二个是全局对象。在一个嵌套的函数体内,它实际上保存一个作用域链。当调用这个函数时,它创建一个新的对象来存储它的局部变量,并将这个对象添加至保存的那个作用链上,同时创建一个新的更长的表示函数调用作用域的“链”。对于嵌套函数来讲,每次调用外部函数时,内部函数又会重新定义一遍。因为每次调用外部函数的时候,作用域链都是不同的。内部函数在每次定义的时候都有微妙的差别——在每次调用外部函数时,内部函数的代码都是相同的,而且关联这段代码的作用域链也不相同。


5.JavaScript中原始值(undefined,null,布尔值,数字和字符串)是不可更改的:任何方法都无法更改一个原始值。
   原始值的比较是值的比较:只有在它们的值相等时它们才相等。而字符串则当且仅当它们的长度相等且每一个索引的字符都相等时,JavaScript才认为它们相等。


     对象和原始值不同,它们是可变的,对象的比较并非值的比较:即使两个对象包含相同的属性以及相同的值,它们也是不相等的。各个索引元素完全相等的两个数组也不相等。当且仅当它们引用同一基对象时,它们才相等。


6.在JavaScript函数里声明的所有变量(但不涉及赋值)都被“提前”至函数体的顶部,也就是将函数内的变量声明“提前”至函数体顶部,同时变量初始化留在原来的位      置。


   当使用var声明一个变量时,创建的这个属性是不可配置的,无法通过delete运算符删除。
   var truevar = 1;
   delete truevar ; // false


7.表达式和运算符
原始表达式:常量或直接量,关键字和变量
如:1.23(数字直接量),"hello"(字符串直接量),/pattern/(正则表达式直接量)
保留字构成的表达式:true,false,null,this
变量构成的表达式:i, sum, undefined 

数组表达式:[];  [1,2,3];  [[1,2,3], [4,5,6], [7,8,9]]
数组直接量的元素列表结尾处可以留下单个逗号,并不会创建一个新的值为undefined的元素。

对象初始化表达式:var p = {x:2.3, y:-1.2};  {upprLeft:{x:2,y:2}, lowerRigth:{x:4,y:5}}

函数定义表达式: var squere = function(x){ return x * x; }

属性访问表达式:o.x  a[1]

调用表达式:Math.max(x, y, z);

对象创建表达式:new Object();


运算符:




运算符的副作用
1.赋值运算符:如果给一个变量或属性赋值,那些使用这个变量或属性的表达式的值都会发生变化。
2."++"和"--":包含了隐式的赋值。
3.delete运算符:删除一个属性就像(但不完全一样)给这个属性赋值undefined


只有在任何一个表达式具有副作用而影响到其他表达式时,其求值顺序才会和看上去有所不同。如果表达式中的一个变量自增1,这个变量在表达式z中使用,那么实际上是先计算出了x的值再计算z的值。
注意:假设存在a = 1,那么 "b = (a++) + a" 的计算顺序为:1.计算b, 2.计算a++(假设为c) 3.计算a, 4.计算c+a, 5.将c+a的结果赋值给b, 按照“++”的定义,因此在执行第3步时,a的值已经是2.所以b的结果为3




"+"运算符:加号的转换规则优先考虑字符串连接。如果两个操作数都不是类字符串的,将进行算术加法运算。
加法操作符行为表现为:
1.如果其中一个操作数是对象,则对象会遵循对象到原始值的转换规则转换为原始类值:日期对象通过toString()方法执行转换,其他对象则通过valueof()方法执行转换(如果valueof()方法返回一个原始值的话)。由于多数对象不具备可用的valueof()方法,因此它们会通过toString()方法来执行转换。
2.在进行了对象到原始值的转换后,如果其中一个操作数是字符串的话,另一个操作数也会转换了字符串,然后进行字符串连接。
3.否则,两个操作数都将转换为数字(或者Nan),然后进行加法操作。


注意:当加号运算符和字符串,数字一起使用时,需要考虑加法的结合性对运算顺序的影响,也就是说,运算结果依赖于运算符的运算顺序,如:
1 + 2 + " blind mice";  // => "3 blind mice"
1 + (2 + " blind mice");  // => "12 blind mice"


严格相等运算符“===”首先计算其操作数的值,然后比较这两个值,比较过程没有任何类型转换:
1.如果两个值类型不相同,则它们不相等。
2.如果两个值都是null或都是undefined,则他们不相等。
3.如果两个值都是布尔值true或都是false,则它们不相等。
4.如果其中一个值是NaN,或者两个值都是NaN,则它们不相等。NaN和其他任何值都是不相等的,包括它们本身。通过x!==x来判断x是否为NaN,只有在x为Nan时这个表达式的值才为true。
5.如果两个值为数字且数值相等,则它们相等。如果一个为0,另一个为-0,则它们相等。
6.如果两个字符串,且所包含的对应位上的16位数完全相等,则它们相等。如果它们的长度或内容不同,则它们不等。两个字符串可能含义完全一样且所显示的字符也一样,但具有不同编码的16位值。JavaScript并不对Unicode进行标准的转换,因此像这样的字符通过“===”和“==”运算符的比较结果也是不相等的。
7.如果两个引用值指向同一个对象,数组或函数,则它们是相等的。如果指向不同的对象,则它们不相等,尽管两个对象具有完全一样的属性。


相等运算符“==”和恒等运算符相似,但相等运算符的比较并不严格。如果两个操作数不是同一类型,那么相等运算符会尝试进行一些类型转换,然后进行比较:
1.如果两个操作数的类型相同,则和上文所述的严格相等的比较规则一样。如果严格相等,那么比较结果相等。如果它们不严格相等,则比较结果为不相等。
2.如果两个操作数类型不同,“==”相等操作符也可能会认为它们相等。检测相等将会遵守如下规则和类型转换:
如果一个值是null,另一个是undefined,则它们相等。
如果一个值是数字,另一个是字符串,将将字符串转换为数字,然后使用转换后的值比较。
如果其中一个值是true,则将其转换为1再进行比较。如果其中一个值是false,则将其转换为0再进行比较。
如果一个值是对象,另一个值是数字或字符串,对象通过toString()方法或valueof()方法转换为原始值。JavaScript语言核心的内置首先尝试 使用valueof(),再尝试使用toString(),除了日期类,日期类只使用toString()转换。那些不是JavaScript语言核心中的对象则通过各自的实现定义的 方法转换为原始值。
其他不同类型之间的比较均不相等。




比较操作符的操作数可能是任意类型,然而,只有数字和字符串才能真正执行比较操作。因此,那些不是数字和字符串的操作数都将进行类型转换,转换规则如下:
如果操作数为对象:如果valueof()返回一个原始值,那么直接使用这个原始值,否则,使用toString()的转换结果进行比较操作。
在对象转换为原始值之后,如果两个操作数都是字符串,那么将按字母表的顺序对两个字符串进行比较,这时提到的“字母表顺序”是指组成
这个字符串的16位Unicode字符的索引顺序。
在对象转换为原始值之后,如果至少有一个操作数不是字符串,那么两个操作数都将转换为数字进行数值比较。0与-0相等。Infinity比其他任何数字都大(除了Infinity本身),-Infinity比其他任何数字都小(除了它自身)。如果其中一个操作数是(或转换后是)NaN,那么比较操作符总是返回false.


对于数字和字符串操作符来说,加号运算符和比较运算符的行为有所不同,前者更偏爱字符串,如果它的其中一个操作数是字符串的话,则进行字符串连接操作。而比较运算符更偏爱数字,只有在两个操作数都是字符串的时候,才会进行字符串的比较。


逻辑与(&&)
如果左操作数是假值,返回左操作数作为整个表达式的值不再计算右操作数,如果左操作数为真值,返回右操作数作为整个表达式的值。如:
var o = { x : 1 };
var p = null;
o && o.x; // => 1
p && p.x; // => null


注意:
var data = [0,1,2];
var i = 1;

data[i++] *= 2;  // => [0,2,2]

data[i++] = data[i++] * 2; //  => [0,4,2]


typeof运算符:


8.语句
switch:
switch语句首先计算switch关键字后的表达式,然后按照从上到下的顺序计算每个case后的表达式,直到执行到case的表达式的值的switch的表达值相等为止。
由于对每个case的匹配操作实际上是“===”恒等运算比较,而不是“==”相等运算比较,因此,表达式和case的匹配并不会做任何类型转换。


标签语句
break和continue是JavaScript中唯一可以使用语句标签的语句。
如:
mainloop:while(token != null){
//
continue mainloop; // 跳转到下一次循环
}




continue语句:
1.在while语句循环中,在循环开始处指定的expression会重复检测,如果检测结果为true,循环体会从头开始执行。
2.在do/while语句中,程序的执行直接跳到循环结尾处,这时会重新判断循环条件,之后才会继续下一次循环。
3.在for循环中,首先计算自增表达式,然后再次检测test表达式,用来判断是否执行循环体。
4.在for/in循环中,循环开始遍历下一个属性名,这个属性名赋给了指定的变量。



9.对象
每个属性都有属性特性:
可写:表明是否可以设置该属性的值
可枚举:表明是否可以通过for/in循环返回该属性
可配置:表明是否可以删除或修改该属性

每个对象还有三个相关对象特性
对象的原型指向另外一个对象,本对象的属性继承自它的原型对象。
对象的类 是一个标识对象类型的字符串
对象的扩展标记 指明了(在ECMAScript5)是否可以向该对象添加新属性。


在这些场景下给对象o设置属性p会失败:
o中的属性p是只读的,不能给只读属性重新赋值(defineProperty()方法中有一个例外,可以给可配置的只读属性重新赋值)
o中的属性p是继承属性,且它是只读的;不能通过同名自有属性覆盖只读的继承属性
o中不存在属性p:o没有使用setter方法继承属性p,并且o的扩展性是false


使用delete,显式指明对象及属性
delete this.x;


属性getter和setter,如:
var person = {
get age() {
if(this.age)
return this.age;
else
return 0;
},
set aget(a) {
this.age = a;
}
};
person.aget = 10;
alert(person.age);
注意:这些函数定义没有使用function关键字,而使用get,set ,属性名和函数体并没有冒号分隔。
和数据属性一样,存取器属性是可以继承的。


属性的特性
数据属性的4个特性分别为它的值(value),可写性(writable),可枚举性(enumerable)和可配置性(configurable),存取器属性的4个特性是读取(get),写入(set),可枚举和可配置。
设置属性的特性:
var o = {};  
object.defineProperty(o, "x", {value : 1, writable : true, enumerable : false, configurable:true}); //设置一个不可枚举的数据属性x,并赋值为1
同时修改或创建多个属性,使用object.defineProperties()。第一个参数为要修改的参数,第二个参数为映射表,包含要新建或修改的属性的名称以及它们的属性特性描述符,返回修改后的对象。如:
var p = Object.defineProperties({},{
x : { value:1, writable:true, enumerable : true, configurable : true },
y : {value:2, writable:true, enumerable : true, configurable : true}, 
r : { get: function(){ return this.r },  writable:true, enumerable : true, configurable : true}

});


对Object.defineProperty()或 Object.defineProperties()违反下面规则的使用都会抛出类型错误:
如果对象是不可扩展的,则可以编辑已有的属性,但不能给它添加新属性。
如果属性是不可配置的,则不能修改它的可配置性和可枚举性。
如果存取器是不可配置的,则不能修改其getter和setter方法,也不能将它转换为数据属性。
如果数据属性是不可配置的,则不能将它转换为存取器属性。
如果数据属性是不可配置的,则不能将它的可写性从false修改为true,但可以从true修改为false
如果数据属性是不可配置且不写的,则不能修改它的值。然而可配置但不可写属性的值是可以修改的(实际上是将它标记 为可写的,然后再修改它的值 , 最后转换为不可写的)


通过Object.getOwnPropertyDescriptor()可以获得某个对象特定属性的属性描述符:
Object.getOwnPropertyDescriptor({x : 1}, "x");
//返回{value : 1, writable:true, enumerable : true, configurable : true}


可扩展性
1.通过将对象传入Object.esExtensible()查询对象是否可扩展。
2.将对象传入Object.perventExtensions()将对象转换为不可扩展的。
  注意:一旦对象转换为不可扩展的,就无法再将其转换为可扩展的了
  如果给一个不可扩展的对象的原型添加属性,这个不可扩展的对象同样会继承这些新属性。


Object.seal()作用:不能给这个对象添加新属性,而且它已有的属性也是不可删除或配置的,但它已有的可写属性依然可以设置。可以用Object.isSealed()检测对象是否封闭。
Object.freeze()除了将对象设置为不可扩展的和其属性设置 为不可配置的外,还可以将它已有的所有数据属性设置为只读(如果对象的存取器具有setter方法,存取器属性将不受影响,仍可以通过属性赋值调用它们。)可以用Object.isFrozen()来检测。


10.数组
用数字索引来访问数组元素一般来说比访问常规的对象属性要快得多。
JavaScript数组有一些特性是其他对象没有的:
当有新的元素添加到列表中是时,自动更新length属性。
设置length为一个较小值将截断数组。
从Array.prototype中继承一些有用的方法
其类属性为“Array”


实际上会出现一些类数组对象,如Arguments对象,一些DOM方法(如document.getElementsByTagName())也返回类数组对象。
JavaScript数组方法是特意定义为通用的,因此它们不仅应用在真正的数组而且在类数组对象上都能正确工作。在ES5中,所有的数组方法都是通用的。ES3中,除了toString()和toLoaleString()以外所有的方法也是通用的。(concat()方法是一个特例:虽然可以用在类数组对象上,但它没有将那个对象扩充进返回的数组中)如:
var a = { "0" : "a", "1" : "b", "2" : c, length:3};  // 类数组对象
Array.prototype.join.call(a, '+'); // "a+b+c"
Array.prototype.slice.call(a, 0); // ["a", "b", "c"] :真正数组的副本

在ES5中,字符串的行为类似于只读数组,除了用chatAt()方法来访问单个字符以外,还可以使用方括号:
var s = "test";
s.chatAt(0); // 't'
s[1]; //'t'


s = "JavaScript";
Array.prototype.join.call(s, ""); // "J a v a S c r i p t";

请记住,字符串是不可变值,如push(),sort(),reverse()和splice()等方法在字符串上是无效的。 

转载于:https://www.cnblogs.com/bin1991/p/3636636.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值