一个完整的JavaScript实现应由以下三个部分组成:
- ECMASript (核心)
- DOM (文档对象模型)
- BOM (浏览器对象模型)
ECMAScript中有五种简单数据类型:
- Underfined
- Null
- Boolean
- Number
- String
一种复杂数据类型:
- Object
注意!
console.log(typeof(null)); //object
with语句会降低性能。
ECMAScript函数命名的参数只提供便利,但不是必须的,不介意传递进去多少个参数,也不在乎传进来参数是什么数据类型。
未指定返回值的函数返回的是一个特殊的undefined值。
ECMAScript中所有的参数传递的都是值,不可能通过引用传递参数。
ECMAScript函数没有签名,因为其参数是由包含零或多个值的数据来表示。因为没有签名,所以没有重载。
JavaScript解释器在执行环境中加载数据时,会率先读取函数声明,使其在执行任何代码之前可用;至于函数表达式,则必须等到解释器执行到它所在的代码行,才会被真正解释执行。
alert(sum(10,10));
function sum(num1, num2){
return num1 + num2;
}
如果将函数声明改为等价的函数表达式,就会在执行期间导致错误。
alert(sum(10,10));
var sum = function(num1, num2){
return num1 + num2;
}
sort函数嵌套:
function createComparisonFunction(propertyName) {
return function(object1, object2){
var value1 = object1[propertyName];
var value2 = object2propertyName];
if(value1 < value2) {
return -1;
} else if (value1 > value2) {
return 1;
} else {
return 0;
}
};
}
var data = {{name:"test", age:28},{name:"hello", age:"25"}};
data.sort(createComparsionFunction("name"));
data.sort(createComparsionFunction("age"));
函数内部有两个特殊的对象: arguments和this,arguments有一个callee的属性,是一个指针指向拥有arguments对象的函数。
//实现递归
function factorial(num) {
if(num<=1){
return 1;
} else {
return num*arguments.callee(num - 1);
}
}
this引用的是函数据以执行的环境对象。当在网页的全局作用域中调用函数时,this引用的是windows.
window.color = "red";
var o = { color: "blue"};
function sayColor(){
alert(this.color);
}
sayColor(); //"red"
o.sayColor = sayColor;
o.sayColor(); //"blue"
ECMAScript中的函数是对象,每个函数都有两个属性:length和prototype.
length表示函数希望接受的命名参数的个数。
prototype是保存它们所有实例方法的真正所在,诸如toString()和valueFO()等方法都保存在prototype下。
每个函数都包含两个非继承来的方法:apply()和call(),两个方法都在特定的作用于中调用函数,实际上等于设置函数体内this对象的值。apply()方法接受两个参数:一个在其中运行函数的作用于,另一个是参数数组。
function sum(num1, num2){
return num1 + num2;
}
function callSum1(num1,num2){
return sum.apply(this, arguments);
}
function callSum2(num1,num2){
return sum.apply(this, [num1,num2]);
}
alert(callSum1(10,10)); //20
alert(callSum2(10,10)); //20
call()方法与apply()作用相同,区别在于接收参数的方式不同。对于call()方法而言,第一个参数是this值,变化的是其与参数都直接传递给函数。
传递参数并非apply()和call()真正用武之地:它们真正强大的是能扩大函数的作用域。
window.color = "red";
var o = {color:"blue"};
function sayColor(){
alert(this.color);
}
sayColor(); //red
sayColor.call(this); //red
sayColor.call(window); //red
sayColor.call(o); //red
bind()
方法创建一个函数的实例,其this值会被绑定到传递给bind()函数的值。
window.color = "red";
var o = {color:"blue"};
function sayColor(){
alert(this.color);
}
var objectSayColor = sayColor.bind(o) ;
objectSayColor(); //blue
引用类型与基本包装类型的主要区别就是对象的生存期。使用 new 操作符创建的引用类型的实例,在执行流离开当前作用域之前都一直保存在内存中。而自动创建的基本包装类型的对象,则只存在于一行代码的执行瞬间,然后立即被销毁。这意味着我们不能在运行时为基本类型值添加属性和方法。来看下面的例子:
var s1 = "some text";
s1.color = ""red;
alert(s1.color); //undefined
ECMA-262 对内置对象的定义是: “由 ECMAScript 实现提供的、不依赖于宿主环境的对象,这些对象在 ECMAScript 程序执行之前就已经存在了。”意思就是说,开发人员不必显式地实例化内置对象,因为它们已经实例化了。
Global(全局)对象可以说是 ECMAScript 中最特别的一个对象了,因为不管你从什么角度上看,这个对象都是不存在的。ECMAScript 中的 Global 对象在某种意义上是作为一个终极的“兜底儿对象来定义的。换句话说,不属于任何其他对象的属性和方法,最终都是它的属性和方法。事实上,没有全局变量或全局函数;所有在全局作用域中定义的属性和函数,都是 Global 对象的属性。
当解析器发现代码中调用 eval()方法时,它会将传入的参数当作实际的 ECMAScript 语句来解析,然后把执行结果插入到原位置。通过 eval()执行的代码被认为是包含该次调用的执行环境的一部分,因此被执行的代码具有与该执行环境相同的作用域链。这意味着通过 eval()执行的代码可以引用在包含环境中定义的变量,举个例子:
//在环境外定义
var msg = "hello world!";
eval('alert(msg)'); //"hello world!"
//在eval环境内定义
eval("function sayHi() { alert('Hi');}");
sayHi();
window ECMAScript 虽然没有指出如何直接访问 Global 对象,但 Web 浏览器都是将这个全局对象作为window 对象的一部分加以实现的。因此,在全局作用域中声明的所有变量和函数,就都成为了 window对象的属性。
操作基本类型值的语句一经执行完毕,就会立即销毁新创建的包装对象。在所有代码执行之前,作用域中就已经存在两个内置对象: Global 和 Math 。在大多数ECMAScript实现中都不能直接访问 Global 对象;不过,Web 浏览器实现了承担该角色的 window 对象。
面向对象程序设计
ECMAScript 中有两种属性:
- 数据属性
- 访问器属性。
- 数据属性
要修改属性默认的特性,必须使用 ECMAScript 5 的 Object.defineProperty() 方法。这个方法接收三个参数:
- 属性所在的对象
- 属性的名字
- 一个描述符对象
其中,描述符(descriptor)对象的属性必须是: configurable 、 enumerable 、 writable 和 value 。设置其中的一或多个值,可以修改对应的特性值。
- 访问器属性
访问器属性不包含数据值;它们包含一对儿 getter 和 setter 函数(不过,这两个函数都不是必需的)。在读取访问器属性时,会调用 getter 函数,这个函数负责返回有效的值;在写入访问器属性时,会调用setter 函数并传入新值,这个函数负责决定如何处理数据。
访问器属性不能直接定义,必须使用Object.defineProperty()
来定义:
vav book = {
_year: 2004,
edition: 1
}
Object.defineProperty(book, "year", {
get: function(){
return this._year;
},
set: function(newValue){
if (newValue>2004) {
this._year = newValue;
this.edition += newValue - 2004;
}
}
});
book.year = 2005;
alert(book.edition); //2
function(){
}