一.javascript 四种数据类型
1 literal values 直接量:字符串,数字,布尔值,数组,函数,对象,正则表达式,具有特殊意义的空值,以及未定义
2 variables 变量 使用var创建的存储数据值
3 Array items 数组项 具有数字索引 存储一个js数组对象
4 object members 对象成员 具有字符串索引 存储一个js对象
总的来说 直接量和局部变量要快于数组项和对象成员(但是有些浏览器对数组项和对象成员进行优化另外一回事)
二.管理作用域
每个javascript函数都被表示为对象或函数实例,拥有你可以编程访问的属性和一些不能被程序访问,仅供javascript引擎使用的内部属性 像[Scope] 包含一个函数被创建的作用域中对象的集合。这集合被称为函数的作用域链,它决定那些数据可由函数访问,函数作用域链中的每个对象称为一个可变对象,每个可变对象都以 健的形式存在。当一个函数创建后,它的作用域链被填充以对象,这些对象代表创建这函数的环境中可访问的数据。
function add(num1, num2){
var sum = num1 + num2;
return sum;
}
当add函数创建后 它的作用域链中填入一个单独的可变对象 这全局对象代表了所有全局范围定义的变量。像窗口,浏览器和文档之类的访问接口都是全局变量。
运行add函数时建立一个内部对象,称作 ‘运行期上下文’ (定义了一个函数运行的环境) 对函数的每次运行而言,每个 ‘运行期上下文’都是独立的 所以多次调用同一个函数就会导致多次的创建运行期上下文 函数执行完毕就会销毁。
一个运行期上下文有它自己的作用域链 用于标识符解析。当运行期上下文被创建时,它的作用域链被初始化,连同运行函数的scope属性中的包含的对象 按照出现在函数的顺序复制到运行期上下文的作用域链中。然后被称作“激活对象”的新对象就为运行期上下文创建好了。 激活对象作为函数执行期的一个可变对象 包含访问所有局部变量 ,命名参数,参数集合 和this的接口。然后这对象被推人作用域链的前端。
标识符识别过程决定从那里获取存储数据。这过程搜索运行期上下文的作用域链,查找同名的标识符。搜索工作从运行函数的激活目标的作用域链的前端开始。所以一个标识符处的位置越深 它的读写速度就越慢。
这样:用局部变量存储本地变量之外的变量值,如果它们在函数中的使用多于一次。
function initUI(){
var bd = document.body,
links = document.getElementsByTagName_r("a"),
i = 0,
len = links.length;
while(i < len){
update(links[i++]);
}
document.getElementById("go-btn").onclick = function(){
start();
};
bd.className = "active";
}
改写:
function initUI(){
var doc = document,
bd = doc.body,
links = doc.getElementsByTagName_r("a"),
i = 0,
len = links.length;
while(i < len){
update(links[i++]);
}
doc.getElementById("go-btn").onclick = function(){
start();
};
bd.className = "active";
}
改变作用域:2种运行期间临时改变运行期作用域链
1.with表达式(避免写重复代码)
function initUI(){
with (document){ //avoid!
var bd = body,
links = getElementsByTagName_r("a"),
i = 0,
len = links.length;
while(i < len){
update(links[i++]);
}
getElementById("go-btn").onclick = function(){
start();
};
bd.className = "active";
}
}
运行with代码块的时候 一个新的可变对象将被创建 它包含指定对象的所有属性。这对象被插入到作用域链的前端,同时意味着函数的所有局部变量都被推人到第二个作用域链对象中。访问的代价更高了。
2.try-catch表达式有相同的效果,将异常对象插入到作用域链的前端,函数的所有局部变量都被推人到第二个作用域链对象中。建议这样写:
try {
methodThatMightCauseAnError();
} catch (ex){
handleError(ex); //delegate to handler method
}
动态作用域:
无论是with表达式还是try-catch表达式 以及包含()的函数 都被称为动态作用域。动态作用域因为运行代码而产生的 而静态分析不了
function execute(code) {
(code);
function subroutine(){
return window;
}
var w = subroutine();
//what value is w?
};
execute("var window = {};") 分析一下 w是全局变量 还是局部变量
所以没有必要不推荐使用动态作用域。
闭包,作用域,内存
闭包 允许访问局部范围之外的数据。
function assignEvents(){
var id = "xdi9592";
document.getElementById("save-btn").onclick = function(event){
saveDocument(id);
};
}
由于闭包的scope属性包含与运行期上下文作用域链相同的对象引用 会产生副作用。通常一个函数的激活对象与运行期上下文一同销毁。但涉及闭包时 激活对象就无法销毁了。因为他的引用仍然存在闭包的scope属性中。这样就需要更多的内存开销,可能导致内存泄漏。
对象成员(文档对象模型和浏览器对象模型)
prototype 原形 为所有给定类型的对象实例所共享。
var book = {
title: "High Performance JavaScript",
publisher: "Yahoo! Press"
};
alert(book.hasOwnProperty("title")); //true
alert(book.hasOwnProperty("toString")); //false
alert("title" in book); //true
alert("toString" in book); //true
原形链 prototype Chains
function Book(title, publisher){
this.title = title;
this.publisher = publisher;
}
Book.prototype.sayTitle = function(){
alert(this.title);
};
var book1 = new Book("High Performance JavaScript", "Yahoo! Press");
var book2 = new Book("JavaScript: The Good Parts", "Yahoo! Press");
alert(book1 instanceof Book); //true
alert(book1 instanceof Object); //true
book1.sayTitle(); //"High Performance JavaScript"
alert(book1.toString()); //"[object Object]"
2个book实例共享同一个原形链,当book1.toString()被调用时 搜索工作必须深入原形链才能找到对象成员的 toString
深入的原形链越深 搜索的速度就会越慢。
嵌套变量
成员嵌套越深 访问速度就越慢 location.href 快于window.location.href 快于window.location.href.toString()
缓存对象成员的值
function hasEitherClass(element, className1, className2){
return element.className == className1 || element.className == className2;
}
改成:
function hasEitherClass(element, className1, className2){
var currentClassName = element.className;
return currentClassName == className1 || currentClassName == className2;
}