JavaScript中全部变量都是对象,除了null和undefinedjavascript
JavaScript对象能够做为哈希表使用,主要用来保存命名的键和值的对应关系html
点操做符和中括号操做符java
中括号操做符在下面2种状况下依然有效 1.动态设置属性 2.属性名不是一个有效的变量名数组
删除属性的惟一方法是使用delete操做符;设置属性为undefined或者null并不能真正的删除属性,只是移除了属性和值的关联浏览器
对象的属性名可使用字符串或者普通字符(非关键词)声明。安全
注:简单的使用Bar.prototype = Foo.prototype将会致使两个对象共享相同的原型。闭包
function Foo(){app
this.value = 42;函数
}性能
Foo.ptototype = {
method : function() {}
};
function Bar() {}
//设置Bar的prototype属性为Foo的实例对象
Bar.prototype = new Foo();
Bar.prototype.foo = "Hello World";
//修正Bar.prototype.constructor为Bar自己
Bar.prototype.constructor = Bar;
注:不要使用Bar.prototype = Foo, 由于这不会执行Foo的原型,而是指向函数Foo。所以原型链将会回溯到Function.prototype而不是Foo.prototype。
查找对象属性时,JavaScript会向上遍历原型链,直到找到给定名称的属性为止。
到查找到达原型链的顶部(Object.prototype),可是仍然没有找到指定的属性,就会返回undefined。
当原型属性用来建立原型链时,能够把任何类型的值赋给它。然而将原子类型赋给prototype的操做将会被忽略。
若是一个属性在原型链的顶端,则对于查找时间将带来不利影响。
当使用 for - in 循环遍历对象的属性时,原型链上的全部属性都将被访问。
一个错误特性被常用,那就是扩展Object.prototype或者其余内置类型的原型对象。
这种技术会破坏封装。
扩展内置类型的惟一理由是为了和新的JavaScript保持一致。好比Arrary.forEach。
函数声明会在执行前被解析(hoisted),所以它存在于当前上下文的任意一个地方,即便在函数定义体的上面被调用也是对的。
foo();
function foo(){}
foo;
foo();
var foo = function(){};
因为var定义了一个声明语句,对变量foo的解析是在代码运行以前,所以foo变量在代码运行时已经被定义过了。
可是因为赋值语句只在运行时执行,所以在相应代码执行以前,foo的值缺省为undefined。
hasOwnProperty是JavaScript中惟一一个处理属性可是不须要查找原型链的方法。
JavaScript不会保护hasOwnProperty被非法占用,所以若是一个对象碰巧存在这个属性,就须要使用外部的hasOwnProperty函数来获取正确的结果。
{}.hasOwnProperty.call(foo, 'bar');
当在所有范围内使用this,它将会指向全局对象。(浏览器中运行的JavaScript脚本,这个全局对象是window)
foo();
此时this指向全局对象。
ES5 注意: 在严格模式下(strict mode),不存在全局变量。这种状况下this将会是undefined。
4.3 方法调用
this指向调用该方法的对象
this指向新建立的对象。
call 或者 apply 方法时,函数内的 this将会被显式设置为函数调用的第一个参数,若为null则指向全局对象
将一个方法赋值给一个变量。
var test = someObject.methodTest;
test();
此时,test就像一个普通的函数被调用;所以,函数内的this将再也不被指向到someObject对象。(晚绑定)
function Counter(start) {
var count = start;
return {
increment: function() {
count++;
},
get: function() {
return count;
}
}
}
var foo = Counter(4);
foo.increment();
foo.get(); // 5
这里,Counter函数返回两个闭包,函数increment和函数get。这两个函数都维持着对外部做用域Counter的引用,所以总能够访问此做用域内定义的变量count。
由于JavaScript中不能够对做用域进行引用或赋值,所以没有办法在外部访问count变量。惟一的途径就是经过那两个闭包。
for(var i = 0; i < 10; i++ ){
setTimeout(function(){
console.log(i);
}, 1000);
}
当console.log被调用的时候,匿名函数保持对外部变量i的引用,此时for循环已经结束,i的值被修改为10。
为了获得想要的结果,须要在每次循环中建立变量i的拷贝。
使用自执行匿名函数
for(var i = 0; i < 10; i++){
(function(e){
setTimeout(function(){
console.log(e);
}, 1000);
})(i);
}
JavaScript中每一个函数内都能访问arguments。它维护着全部传递到这个函数中的参数列表。
经过var关键字定义arguments或者将arguments声明为一个形式参数,都将致使原生的arguments不会被建立。
arguments变量不是一个数组,尽管在语法上有个数组相关的属性length。
Array.prototype.slice.call(arguments)
function foo(){
bar.apply(null, arguments);
}
function bar(a, b, c){
//some work
}
arguments对象总会被建立,除了两个特殊状况 - 做为局部变量声明和做为形式参数。
使用arguments.callee会显著的影响现代JavaScript引擎的性能。
ES5提示:在严格模式下,arguments.callee会报错TypeError,由于它已经被废除了。
在构造函数内部,this指向新建立的对象Object。这个新建立的对象的prototype被指向到构造函数的prototype。
被调用的函数没有显示的return表达式,则隐式的会返回this对象。
显示的return表达式将会影响返回结果,但仅限于返回的是一个对象。
function Bar(){
return 2;
}
new Bar(); //返回新建立的对象 new Bar().constructor === Bar
function Foo(){
this.value = 2;
return {
foo : 1
};
}
new Foo(); //返回的对象 {foo:1}
function Foo(){
var obj = {};
obj.value = "blue";
var privateVal = 2;
obj.someMethod = function(value){
this.value = value;
}
obj.getPrivate = function(){
return privateVal;
}
return obj;
}
优势:充分利用私有变量,比起使用构造函数方式不容易出错。
缺点:占用更多的内存,新建立的对象不能共享原型上的方法。
为了实现继承,工厂方法须要从另一个对象拷贝全部属性,或者把一个对象做为新建立对象的原型。
放弃原型链仅仅是由于防止遗漏new带来的问题,这彷佛和语言自己的思想一想违背。
JavaScript不支持块级做用域,而仅仅支持函数做用域。
注意: 若是不是在赋值语句中,而是在return表达式或者函数参数中,{...}
将会做为代码段解析, 而不是做为对象的字面语法解析。若是考虑到自动分
号插入,这可能会致使一些不易察觉的错误。若是return对象的左括号和return不在一行上就会出错。
两种方式声明,一个事做为函数参数,另外一个是经过var关键字声明。
JavaScript会提高变量声明。这意味着var表达式和function声明都将会被提高到当前做用域的顶部。
当访问函数内的foo变量时,JavaScript会按照下面顺序查找:
1.当前做用域内是否有var foo的定义。
2.函数形式参数是否有使用foo名称的。
3.函数自身是否叫作foo。
4.回溯到上一级做用域,而后从 1 从新开始
经过当即执行的匿名函数解决命名冲突的问题。
(function(){
//do some work
})();
使用for - in循环,会查询对象原型链上的全部属性,所以须要使用hasOwnProperty函数来过滤,比普通for循环慢上好几倍。
length属性的getter方式会简单的返回数组的长度,而setter方式会截断数组。
var foo = [1, 2, 3, 4, 5, 6];
foo.length = 3;
foo; // [1, 2, 3]
foo.length = 6;
foo; // [1, 2, 3]
译者注: 在 Firebug 中查看此时 foo 的值是: [1, 2, 3, undefined,
undefined, undefined] 可是这个结果并不许确,若是你在 Chrome 的控制台
查看 foo 的结果,你会发现是这样的: [1, 2, 3] 由于在 JavaScript 中
undefined 是一个变量,注意是变量不是关键字,所以上面两个结果的意义是
彻底不相同的。
[1, 2, 3]; // 结果: [1, 2, 3]
new Array(1, 2, 3); // 结果: [1, 2, 3]
[3]; // 结果: [3]
new Array(3); // 结果: []
new Array('3') // 结果: ['3']
因为只有一个参数传递到构造函数中(译者注:指的是 new Array(3); 这种调
用方式),而且这个参数是数字,构造函数会返回一个 length 属性被设置为
此参数的空数组。 须要特别注意的是,此时只有 length 属性被设置,真正的
数组并无生成。
等于操做符由两个等号组成:==
JavaScript 是弱类型语言,这就意味着,等于操做符会为了比较两个值而进行强制类型转换。
"" == "0" // false
0 == "" // true
0 == "0" // true
false == "false" // false
false == "0" // true
false == undefined // false
false == null // false
null == undefined // true
" \t\r\n" == 0 // true
此外,强制类型转换也会带来性能消耗,好比一个字符串为了和一个数组进行比较,必须事先被强制转换为数字。
严格的等于操做符由三个等号组成:===
不想普通的等于操做符,严格的等于操做符不会进行强制类型转换。
"" === "0" // false
0 === "" // false
0 === "0" // false
false === "false" // false
false === "0" // false
false === undefined // false
false === null // false
null === undefined // false
" \t\r\n" === 0 // false
虽然 == 和 === 操做符都是等于操做符,可是当其中有一个操做数为对象时,行为就不一样了。
{} === {}; // false
new String('foo') === 'foo'; // false
new Number(10) === 10; // false
var foo = {};
foo === foo; // true
这里等于操做符比较的不是值是否相等,而是是否属于同一个身份;也就是说,
只有对象的同一个实例才被认为是相等的。 这有点像 Python 中的 is 和 C
中的指针比较。
Value Class Type
-------------------------------------
"foo" String string
new String("foo") String object
1.2 Number number
new Number(1.2) Number object
true Boolean boolean
new Boolean(true) Boolean object
new Date() Date object
new Error() Error object
[1,2,3] Array object
new Array(1, 2, 3) Array object
new Function("") Function function
/abc/g RegExp object (function in Nitro/V8)
new RegExp("meow") RegExp object (function in Nitro/V8)
{} Object object
new Object() Object object
Class一列表示对象的内部属性[[Class]]的值。
为了获取对象的[[Class]],咱们须要使用定义在 Object.prototype上的方法toString。
JavaScript 标准文档中定义: [[Class]] 的值只多是下面字符串中的一个:
Arguments, Array, Boolean, Date, Error, Function, JSON, Math, Number, Object, RegExp, String。
JavaScript标准文档只给出了一种获取[[Class]]值的方法,那就是使用Object.prototype.toString。
function is(type, obj) {
var clas = Object.prototype.toString.call(obj).slice(8, -1);
return obj !== undefined && obj !== null && clas === type;
}
is('String', 'test'); // true
is('String', new String('test')); // true
ES5 提示: 在 ECMAScript 5 中,为了方便,对 null 和 undefined 调用
Object.prototype.toString方法,其返回值由Object变成了Null和Undefined。
instanceof操做符用来比较两个操做数的构造函数。只有在比较自定义的对象时才有意义。若是用来比较内置类型,将会和typeof操做符同样用处不大。
function Foo() {}
function Bar() {}
Bar.prototype = new Foo();
new Bar() instanceof Bar; // true
new Bar() instanceof Foo; // true
// 若是仅仅设置Bar.prototype为函数Foo自己,而不是Foo构造函数的一个实例
Bar.prototype = Foo;
new Bar() instanceof Foo; // false
new String('foo') instanceof String; // true
new String('foo') instanceof Object; // true
'foo' instanceof String; // false
'foo' instanceof Object; // false
new Number(10) === 10; // False, 对象与数字的比较
Number(10) === 10; // True, 数字与数字的比较
new Number(10) + 0 === 10; // True, 因为隐式的类型转换
使用内置类型Number做为构造函数将会建立一个新的Number对象,而在不使用new关键字的Number函数更像是一个数字转换器.
最好的选择是把要比较的值显式的转换为三种可能的类型之一。
'' + 10 === '10'; // true
将一个值加上空字符串能够轻松转换为字符串类型。
+'10' === 10; // true
使用一元的加号操做符,能够把字符串转换为数字。
其余字符串转换为数字的经常使用方法:
+'010' === 10
Number('010') === 10
parseInt('010', 10) === 10 // 用来转换为整数
+'010.2' === 10.2
Number('010.2') === 10.2
parseInt('010.2', 10) === 10
经过使用否操做符两次,能够把一个值转换为布尔型。
!!'foo'; // true
!!''; // false
!!'0'; // true
!!'1'; // true
!!'-1' // true
!!{}; // true
!!true; // true
在任何状况下咱们都应该避免使用eval函数。99.9%使用eval的场景都有不使用eval的解决方案。
eval 也存在安全问题,由于它会执行任意传给它的代码,在代码字符串未知或者是来自一个不信任的源时,绝对不要使用eval函数。
undefined是一个值为undefined的类型。
下面的状况会返回 undefined 值:
访问未修改的全局变量 undefined。
因为没有定义 return 表达式的函数隐式返回。
return 表达式没有显式的返回任何内容。
访问不存在的属性。
函数参数没有被显式的传递值。
任何被设置为 undefined 值的变量。
因为全局变量undefined只是保存了undefined类型实际值的副本,所以对它赋新值不会改变类型undefined的值。
为了不可能对 undefined 值的改变,一个经常使用的技巧是使用一个传递到匿名包装器的额外参数。在调用时,这个参数不会获取任何值。
var undefined = 123;
(function(something, foo, undefined) {
// 局部做用域里的 undefined 变量从新得到了 `undefined` 值
})('Hello World', 42);
另一种达到相同目的方法是在函数内使用变量声明。
var undefined = 123;
(function(something, foo) {
var undefined;
...
})('Hello World', 42);
JavaScript 中的 undefined 的使用场景相似于其它语言中的 null,实际上JavaScript 中的 null 是另一种数据类型。
它在 JavaScript 内部有一些使用场景(好比声明原型链的终结Foo.prototype = null ),可是大多数状况下均可以使用 undefined 来代替。
16.setTimeout 和 setInterval
基于JavaScript引擎的计时策略,以及本质上的单线程运行方式,因此其它代码的运行可能会阻塞此线程。所以无法确保函数会在setTimeout指定的时刻被调用。
做为第一个参数的函数将会在全局做用域中执行,所以函数内的 this 将会指向这个全局对象。
function Foo() {
this.value = 42;
this.method = function() {
// this 指向全局对象
console.log(this.value); // 输出:undefined
};
setTimeout(this.method, 500);
}
new Foo();
注意: setTimeout的第一个参数是函数对象,一个常犯的错误是这样的setTimeout(foo(), 1000) , 这里回调函数是foo的返回值,而不是foo自己。
大部分状况下,这是一个潜在的错误,由于若是函数返回undefined,setTimeout也不会报错。
当回调函数的执行被阻塞时,setInterval 仍然会发布更多的毁掉指令。在很小的定时间隔状况下,这会致使回调函数被堆积起来。
function foo(){
// 阻塞执行 1 秒
}
setInterval(foo, 100);
上面代码中,foo 会执行一次随后被阻塞了一分钟。
在 foo 被阻塞的时候,setInterval 仍然在组织未来对回调函数的调用。 所以,当第一次 foo 函数调用结束时,已经有10次函数调用在等待执行。
最简单也是最容易控制的方案,是在回调函数内部使用 setTimeout 函数
function foo(){
// 阻塞执行 1 秒
setTimeout(foo, 100);
}
foo();
能够经过将定时时产生的 ID 标识传递给clearTimeout或者clearInterval函数来清除定时。
setTimeout 和 setInterval 也接受第一个参数为字符串的状况。 这个特性绝对不要使用,由于它在内部使用了 eval。
详情可见:JavaScript秘密花园