1、浏览器一般会按照<script>元素在页面中出现的先后顺序对它们依次进行解析。
2、设置了defer="defer"属性的<script>表明脚本在执行时不会影响页面的构造。也就是说,脚本会被延迟但整个页面解析完毕后再运行。HTML5规范要求脚本按照它们出现的先后顺序执行,而且要先于DOMContentLoaded事件执行。(但在现实中并不能保证这两点。因此最好只包含一个延迟脚本)
3、设置了ansyc属性的<script>脚本与defer类型,但不保证按照多个文件按照出现的先后顺序执行,因此要确保多个文件之间互不依赖,建议异步脚本不要在加载期间修改DOM。异步脚本一定会在load事件前执行,但可能会在DOMContentLoaded事件触发之前或之后。(Firefox 3.6, Safari 5, chrome支持ansyc)
1. var 的作用
Var 在JavaScript中是声明变量的作用,当然因为JavaScript是松散语言,所有也可以不使用var声明变量。
// These are both globals
var foo = 1;
bar = 2;
function test()
{
var foo = 1;// Local
bar = 2; // Global
// Execute an anonymous function
(function()
{
var wibble = 1;// Local
foo = 2;// Inherits from scope above (creating a closure)
moo = 3;// Global
}())
}
上面这个示例告诉我们,如果你不用var,那么这个js引擎会一层一层地向上找父作用域中的变量,如果找到了,就用,如果找不到了,就会帮你定义一个全局的变量。上面这个例子充分说明了这一点。所以,如果你想在当前的作用域用声明变量,你一定要用var。
参考:http://coolshell.cn/articles/7480.html
2. JS变量
2.1 局部变量先使用后声明,不影响外部同名变量。
var x = 1;// --> 外部变量x
fn(); //可以在声明前调用,不推荐
function fn(){
alert(x); // --> undefined 局部变量x先使用
var x = 2;// 后声明且赋值
alert(x); // 2
}
alert(x);// --> 1
在函数 fn 中由于声明了变量x,所有会把全局变量x给覆盖掉。在第一个alert(x)中,因为并没有对局部变量x赋值,因此结果是 underfined。在第二个alert(x)中,显示的是局部变量x的值。在第三个alert(x)中显示的是全局变量x的值。
2.2 同名变量指向同一块内存
var str = "zzj";
function fn(a)
{
var a;
alert(a); //hello
alert(arguments[0]);//hello
var a = 1;
alert(a); //1
alert(arguments[0]);//1
}
fn("hello");
var str;
alert(str); // zzj
在函数中可以通过形参名和arguments来访问传入的值,它们是指向同一块内存的,我们虽然声明了多个与形参同名的变量,但可以发现,同名变量时指向同一块内存。
3. JavaScript的词法作用域
函数在定义它们的作用域里运行,而不是在执行它们的作用域里运行。
调用对象位于作用域链的前端,局部变量(在函数内部用var声明的变量)、函数参数及Arguments对象都在函数内的作用域中——这意味着它们隐藏了作用域链更上层的任何同名的属性。
var i = 1;
function a() {
alert(i); // undefined
var i = 2;
alert(i); // 2
}
a();
参考:http://www.cnblogs.com/mophee/archive/2009/03/15/1412590.html
4. 闭包
闭包是定义在函数内部,并能够读取其他函数内部变量的函数。
JS里的function能访问它们的:
1. 参数
2. 局部变量或函数
3. 外部变量(环境变量?),包括
3.1 全局变量,包括DOM。
3.2 外部函数的变量或函数。
如果一个函数访问了它的外部变量,那么它就是一个闭包。
闭包的用途:
闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
var db = (function() {
// 创建一个隐藏的object, 这个object持有一些数据
// 从外部是不能访问这个object的
var data = {};
// 创建一个函数, 这个函数提供一些访问data的数据的方法
return function(key, val) {
if (val === undefined) { return data[key] } // get
else { return data[key] = val } // set
}
// 我们可以调用这个匿名方法
// 返回这个内部函数,它是一个闭包
})();
db('x'); // 返回 undefined
db('x', 1); // 设置data['x']为1
db('x'); // 返回 1
// 我们不可能访问data这个object本身
// 但是我们可以设置它的成员
使用闭包的注意点:
1、由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2、闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
参考:http://kb.cnblogs.com/page/110782/
http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html
5. 继承和原型链
5.1 对象冒充
function ClassA(sColor) {
this.color = sColor;
this.sayColor = function () {
alert(this.color);
};
}
function ClassB(sColor, sName) {
this.newMethod = ClassA;
this.newMethod(sColor);
delete this.newMethod;
this.name = sName;
this.sayName = function () {
alert(this.name);
};
}
var objA = new ClassA("blue");
var objB = new ClassB("red", "John");
objA.sayColor(); //输出 "blue"
objB.sayColor(); //输出 "red"
objB.sayName(); //输出 "John"
对象冒充还可以实现多重继承
function ClassZ() {
this.newMethod = ClassX;
this.newMethod();
delete this.newMethod;
this.newMethod = ClassY;
this.newMethod();
delete this.newMethod;
}
在多重继承中如果存在两个类 ClassX 和 ClassY 具有同名的属性或方法,ClassY 具有高优先级。因为它从后面的类继承。除这点小问题之外,用对象冒充实现多重继承机制轻而易举。
Call() 方法
function ClassB(sColor, sName) {
//this.newMethod = ClassA;
//this.newMethod(color);
//delete this.newMethod;
ClassA.call(this, sColor);
this.name = sName;
this.sayName = function () {
alert(this.name);
};
}
Apple()方法
function ClassB(sColor, sName) {
//this.newMethod = ClassA;
//this.newMethod(color);
//delete this.newMethod;
ClassA.apply(this, arguments);
this.name = sName;
this.sayName = function () {
alert(this.name);
};
}
Call() 和 apple() 方法是的使用对象冒充方法继承代码更简洁。
5.2 原型链
prototype 对象是个模板,要实例化的对象都以这个模板为基础。总而言之,prototype 对象的任何属性和方法都被传递给那个类的所有实例。原型链利用这种功能来实现继承机制。
function ClassA() {
}
ClassA.prototype.color = "blue";
ClassA.prototype.sayColor = function () {
alert(this.color);
};
function ClassB() {
}
ClassB.prototype = new ClassA(); //确保构造函数没有任何参数
ClassB.prototype.name = "";
ClassB.prototype.sayName = function () {
alert(this.name);
};
原型链的弊端是不支持多重继承。记住,原型链会用另一类型的对象重写类的 prototype 属性。
5.3 混合方式
对象冒充的主要问题是必须使用构造函数方式,这不是最好的选择。不过如果使用原型链,就无法使用带参数的构造函数了。
创建类的最好方式是用构造函数定义属性,用原型定义方法。这种方式同样适用于继承机制,用对象冒充继承构造函数的属性,用原型链继承 prototype 对象的方法。
function ClassA(sColor) {
this.color = sColor;
}
ClassA.prototype.sayColor = function () {
alert(this.color);
};
function ClassB(sColor, sName) {
ClassA.call(this, sColor);
this.name = sName;
}
ClassB.prototype = new ClassA();
ClassB.prototype.sayName = function () {
alert(this.name);
};
var objA = new ClassA("blue");
var objB = new ClassB("red", "John");
objA.sayColor(); //输出 "blue"
objB.sayColor(); //输出 "red"
objB.sayName(); //输出 "John"
参考:http://www.w3school.com.cn/js/pro_js_inheritance_implementing.asp