2. ==(相等,equal)和===(全等,strictly equal)的差别:
2a.因为JS可以自动做类型转换,所以1==”1″ 但是1 !== “1”, 所以对两个值类型之间的比较,相等只要转换后一样就行。全等则必须类型也相同。
2b.如果是两者都是引用类型,他们之间的比较不会自动转换,则相等就代表了引用对象地址相同,类型也相同(必须都是引用),所以也必然全等。
2c.如果一个值类型和一个引用类型之间比较是否相等(==),会自动把引用先转成值类型再比较值。全等根据定义必然为false
2d. 还有, null(对象/函数,引用类型) == undefined(自成一类的值类型)。这个也是可以自动转换的。
var str = ‘abcde’;
var obj = new String(str);
但是,在赋值运算时,虽然字符串是值类型,JavaScript宿主解释程序却不会直接修改变量指向的内存地址,而是变成拷贝一个新字符串的地址过来。这又和引用类型是一样的,而且JavaScript中字符串类型也是一旦生成,就不能修改其中的字符的“不可变”类型。所以所谓的值类型仅仅是在上面的代码里会体现出无法修改属性的特点而已。
4. 正则表达式老生常谈:RegExp也是一种类型,表达式直接量格式是/pattern/modifiers , 参见: https://www.w3schools.com/Jsref/jsref_obj_regexp.asp, 也可以用new RegExp(‘pattern’,’modifiers ‘);来创建
5.神奇的成员访问运算符[]:1.5.3. 属性存取与方法调用 中提到:JavaScript 并不这样,它是通过“ 运算” 来实现面向对象特性的典型。例如我们在这一小节要说到的属性存取与方法调用…… “ .”与“ [ ]”是两个运算符。… 具体到语法的实现方法上,只需要使得“ .” 和“ [ ]” 运算的优先级高于“ ( )” 即可 。把它们看成用于寻找定位对象成员的运算符,这就是Javascript精妙的地方。不需要对象指针,函数指针,虚函数,虚表这些东西在实现机制上的帮助,”[]”实现成运算符,且能接受变量、直接量或表达式作为运算元后(而不仅仅是.能接受的标识符),即使Obj[‘methodA’]()这样的写法,已经足够定位到成员并且作为函数运行之。这就是解释型语言的优势。C++中”[]”运算符却不是用来做member access的。http://www.cplusplus.com/doc/tutorial/operators/ . 这样也就很好理解JavaScript的数组类型了,数组类型就是数字(下标)作为成员名的可增长的对象类型(JS六大类型中没有数组和正则表达式,他们都是object的一种,和String类一样的地位),所以JavaScript不存在连续存放的多维数组,“多维数组”只是某个位置的成员又是一个数组类型而已。
6. Javascript中的可比较序列的类型(可以应用< <=, > >=)有:数字,布尔,和字符串,由于可以类型转换,字符串除非和字符串比的时候是按字符一个个比,和其他类型比都是转成数字来比(布尔也是数字),不能转成数字的串会被转成NaN。任何东西和undefined或者引用类型比都是返回false.NaN和任何数字比也返回false
7. GOTO 语句没有被启用, 所以Label 只能用于 break 和 continue 关键字之后。 不带标签的 break” 仅能用于循
环(和 switch分支)的内部并且只退出最内层,带了标签的break可以用于该标签语句的block内的break(这个break必须用label指明跳出范围):
my_label: {
if (str && str.length < 10) {
break my_label;
}
str = str.substr(str.length-10);
}
因为没有goto, Label除了用于block语句开始,其他就只有用于循环和swtich语句开始时有用了,否则写了label也没有地方能跳转过来。如果把循环和switch的block都看成block,那么可以说label就是在各种block开始处才有用。
8. 没有引用的匿名函数的调用方法(一次过的调用):
// 示例3. 没有引用的匿名函数的调用方法(1)
(function() {
// …
}());
// 示例4. 没有引用的匿名函数的调用方法(2)
(function() {
// …
})();
// 示例5. 没有引用的匿名函数的调用方法(3)
void function() {
// …
}();
感觉用void把它当作表达式运算而不保留值最直观,没有额外的括号需要数是不是匹配。但平时代码中可能还是示例4看见得比较多些。直接function(){…}()不行是因为()前面的部分已经被解释成函数直接量语句了,后面就是一个空的括号语句(),JS支持没有分号的写法,解释器尝试完所有的解释得到了这种结果。但是作者是声称的空括号语句不行,实际上一个函数直接量做语句也不行,比如(1,2);是合法的语句,但function(){…}(1,2);后面的语句跑不到,说明是前面的function声明被当作语句是不行的(加个括号变表达式就不会报错,再加函数运算符括号对才会被执行)。
9 又一个神奇的运算符new, Javascript将 new 右边的运算元视为构造器, 传入代表新对象的this指针(或者我们叫this对象引用比较好),构造器可以是函数,内部可以修改this引用对象的属性;也可以不对this对象做任何事情返回一个新对象,只是new 运算符浪费掉了;构造器也可以是表达式: https://jsfiddle.net/yww325/cty1oyLh/ . 但后两个都不是构造器的主(zheng)流(que)用法(没有利用new来的this引用)。
11. 循环列举 for…in 语句(和in 运算符是两回事,虽然都和检测属性相关).
11a. constructor, 数组对象的length属性等这些内置属性in运算符返回true,但是循环列举 for…in 语句循环不出来.
11b.for…in 语句字面上要求属性是enumerable的,和 obj.propertyIsEnumerable()=true|false的结果本来应该一致。但是for…in语句可以列出继承的父类(通过prototype)上定义的属性,但是ECMAScript规范要求propertyIsEnumerable方法只检测对象的非继承属]性。这就比for…in的少了,有in运算符的只要包括propertyName in this.constructor.prototype就行了,老引擎实现propertyIsEnumerable时可以用this[propertyName] !== this.constructor.prototype[propertyName]来剔除掉继承来的引用属性(全等),但是如果是值类型属性呢?似乎应该检查constructor.prototype[propertyName]==undefined更准确,但是也没法区分值就是undefined的值属性(如果没有in运算符)。
12. 解释器对语句优先:
12a:
var code = ‘if (true) { entry : 1 }’; //entry is label
var value = eval(code);
alert( value ); // 显示值1
12b: var a = 1, 2, 3; // a并不等于3,没有解释成序列表达式,var声明会使连续运算表达式变为连续声明语句(优先)
13. 命令式语言的抽象层次不断提高:从命令式起步,面向对象是命令式语言的扩展, 最终就是“接口首次从系统或模块中剥离了“ 数据”的概念,进而把与数据有关的关系也清理了出去——例如引用(对象间的引用是面向对象体系的灾难之源)” 。 接口基于的原则并不是“ 结构确定,则算法确定”,而是“ 在公同的规约描述下的(算法的)功能,是确定的”。所以我认为,接口中的方法最好不要引用任何类,接口中的方法的参数也应该是接口,把对象(类)的使用清理出去。
而 JavaScript则支持三种编程范型:函数式、命令式和(基于原型的)面向对象。(虽然没有使用接口的概念,但是可以用类对象来当接口用)
14. 复合语句是大括号{}括起来可以和普通语句替换的地方;批语句则是必须要大括号,不能用一句语句的地方,比如try, swtich. 语句里不能嵌函数声明(如果语句包含了函数声明,引擎会当作函数是在语句之后紧跟着声明的),但是可以用函数表达式或者函数执行语句。
15. SpiderMonkey里引入的let就是在Block级别的变量作用域限定符,block级别包括复合语句和批语句,以及函数声明等各种大括号。
16. JavaScript 环境中开发一些大型项目时,“ 随时随地”都可以从函数内泄露到全局的命名(函数内忘了写var),以及随在函数内对全局变量可能发生的修改,总在威胁着项目的品质。
18. 因为在 JavaScript 中,语法解释与执行分成两个阶段,而变量声明是在语法解释阶段处理的,因此在代码执行之前,这个变量就已经在它的语法作用域在存在了。这带来两点影响:
18a.同一个变量多次用var重复声明并不违法,不声明直接用也可以被引擎理解(临时创建成undefined, 而如果有var则在执行开始前,解释阶段后就已经创建成undefined,总之,不管有没有var,第一次创建都是成undefined)。
18b. 函数声明也是在解释阶段就注入了。所以如:
TMyClass = Class(TObject, _ConstructorName(foo));
function foo() {
// …
this.Class = TMyClass;
}
在这个例子中,由于 foo 是一个函数声明的标识符,所以在执行期的任意
位置都可以直接使用。在顺序上,使用 foo 这个标识可以早于该标识符的声明,
这使得 JavaScript不必使用专门的语法来处理前置声明(例如 pascal语法中的
forword) 。同样的原因,在 JavaScript 中,对象可以轻松地持有它自身,或者
它的类——只要它们在可视的局部或全局范围内,被显式地声明过。
19 “构造器的prototype 属性总是来自于 new Object()产生的实例” 这是说new运算符后面的构造器不但产生了Object实例,而且也对prototype的属性做了访问,设置了新对象的prototype.constructor为Object函数(或别的什么函数,对应的也就是产生了别的什么函数的对象实例)。JavaScript 中的对象实例本质上只是“ 一个指向其原型函数(prototype)的,并持有一个成员列表的结构” , 任意对象的prototype属性就是一个普通的“空的对象”, 其constructor属性被设过,所以任意对象实际上含着这样一个子对象+自己的成员列表结构(经常在prototype函数里被初始化,但是也可以任意时刻追加或者删除)
var __proto__ = null;
function get_prototype() {
if (!_proto_) {
__proto__ = new Object();
__proto__.constructor = this; //this 是Object()函数或自定义的任何MyObject()函数
}
return _proto_;
}
20. 普通的“空的对象”一点也不空,本身有很多Object.prototype.内置方法,比如valueOf(),hasOwnProperty()等可以被子对象调用。和Object.getOwnPropertyNames(), getOwnPropertyDescriptor()等一堆Object的静态方法要互相区分。
21. 函数(通过function关键字声明或者new Function()构造的变量)本身不作为构造器而是作为一个对象来看的时候,它比“空的对象”也就是常规Object对象要更进一步,它的prototype多加了bind(), call()等内置方法,而且还有如arguments和name这些属性
22. Javascript中通过一个普通函数来创建它的对象实例的几种方法: