《JS高级程序设计》Note3-Object Type, Function Type

Object Type

创建Object实例

创建Object实例的方法有两种

Object构造函数

 
var person = new Object();
person.name = "Nicholas";
person.age = 29;

对象字面量

 
var person = {
    name : "Nicholas", // 用逗号隔开各个属性
    age : 29   // 此处无逗号
};

使用对象字面量时,属性名可以是字符串,如下

 
var person = {
    "name" : "Nicholas",
    "age" : 29,
    5: true
};

此处name,age,5都会作为字符串处理

如下的定义方式也是正确的

 
var person = {}; //same as new Object()
person.name = "Nicholas";
person.age = 29;

通过对象字面量定义对象时,不会调用Object构造函数

另外,对象字面量也是向函数传递大量可选参数的首选方法

 
function displayInfo(args) {
    var output = "";
                   
    if (typeof args.name == "string"){
        output += "Name: " + args.name + "\n";
    }
                   
    if (typeof args.age == "number") {
        output += "Age: " + args.age + "\n";
    }
                   
    alert(output);
}
                   
displayInfo({
    name: "Nicholas",
    age: 29
});
                   
displayInfo({
    name: "Greg"
});

上面的代码中,我们先定义了一个displayInfo函数,然后两次调用该函数,两次调用所传递的参数都不同

上面的方式最适合传递若干可选参数的情形。

访问对象的属性

访问对象的属性可以有两种方式,除了点表示法,还可以使用方括号表示法。不过属性名应该以字符串的形式放在方括号中 如下

 
alert(person["name"]); //"Nicholas"
alert(person.name); //"Nicholas"

以上两种方法没有区别,但方括号语法的优点是可以通过变量的方式访问,如下

 
var propertyName = "name";
alert(person[propertyName]); //"Nicholas"

如果属性名中包含导致语法错误的字符,或者关键字,保留字,则可以使用方括号表示法,如下

 
person["first name"] = "Nicholas";

除了特别情况,一般推荐使用点表示法

Function Type

javascript中的函数也是对象,函数名是指向函数对象的指针,所以,javascript中的重载其实只是 函数名指向另一个函数而已。

定义函数可以有以下三种方式

using function-declaration syntax

 
function sum (num1, num2) {
    return num1 + num2;
}

using a function expression

 
var sum = function(num1, num2){ 
    return num1 + num2; 
};

using the Function constructor

这种方式不推荐使用。这种语法能很好解释函数时一种对象。

 
var sum = new Function("num1", "num2", "return num1 + num2"); //not recommended

以上表达式中,Function的最后一个参数是函数体,其他为函数参数。

由于函数名只是函数对象的指针,那么一个函数可以有多个名字(一个函数对象可以有多个函数指针),如下

 
function sum(num1, num2){
    return num1 + num2;
}
alert(sum(10,10)); //20
                   
var anotherSum = sum;
alert(anotherSum(10,10)); //20
                   
sum = null;
alert(anotherSum(10,10)); //20

No Overloading (没有重载)

看如下例子

 
function addSomeNumber(num){
    return num + 100;
}
                   
function addSomeNumber(num) {
    return num + 200;
}
                   
var result = addSomeNumber(100); //300

我们貌似对addSomeNumber(num)进行了重载,然后进行调用,最后调用到了第二个函数, 其实此处没有重载,下面代码等同于上面代码,

 
var addSomeNumber = function (num){
    return num + 100;
};
                   
addSomeNumber = function (num) {
    return num + 200;
};
                   
var result = addSomeNumber(100); //300

很明显,所谓的”重载”,只是函数对象被重新定义了而已,而非重载。

Function Declarations versus Function Expressions (函数声明和函数表达式)

如下代码

 
alert(sum(10,10));
function sum(num1, num2){
    return num1 + num2;
}

函数sum声明在调用之后,但是能正常执行。

Function Declarations 与 Function Expressions 之间有一个区别,就是Function Declarations 中有函数声明提升(function declaration hoisting.) 在代码执行之前,解析器会将函数声明添加到执行环境,并放到源代码树的顶部。

再看如下代码,

 
alert(sum(10,10));   //unexpected identifi er
var sum = function(num1, num2){
    return num1 + num2;
};

如上代码使用函数表达式,则会出错。

Functions as Values

函数作为值,可以作为值传入给某个函数,也可以作为某个函数的返回值 (This means it’s possible not only to pass a function into another function as an argument but also to return a function as the result of another function.)

pass a function into another function

 
function callSomeFunction(someFunction, someArgument){
    return someFunction(someArgument);
}

function add10(num){
    return num + 10;
}
                   
var result1 = callSomeFunction(add10, 10);
alert(result1); //20
                   
function getGreeting(name){
    return "Hello, " + name;
}
                   
var result2 = callSomeFunction(getGreeting, "Nicholas");
alert(result2); //"Hello, Nicholas"

上面代码定义函数callSomeFunction,它能接收2个参数,一个是某个函数指针x,一个是函数x的参数, 然后在callSomeFunction执行函数x 。

return a function as the result of another function

以下函数返回结果是一个函数,这个返回函数主要是对object1,object2两个对象的某个属性进行比较。 该函数常用于数组的排序

 
function createComparisonFunction(propertyName) {
                   
    return function(object1, object2){
        var value1 = object1[propertyName];
        var value2 = object2[propertyName];
                   
        if (value1 < value2){
            return -1;
        } else if (value1 > value2){
            return 1;
        } else {
            return 0;
        }
    };
}


var data = [{name: "Zachary", age: 28}, {name: "Nicholas", age: 29}];
                   
data.sort(createComparisonFunction("name"));
alert(data[0].name); //Nicholas
                   
data.sort(createComparisonFunction("age"));
alert(data[0].name); //Zachary

Function Internals (函数内部属性)

Two special objects exist inside a function: arguments and this。

arguments

arguments 对象代表传递给函数的参数数组,它也是一个对象,它有一个特殊的属性: callee callee此处其实也是个函数,但是在javascript中函数也是对象,所以此处也称为属性。 callee指向拥有arguments对象的对象( the arguments object also has a property named callee, which is a pointer to the function that owns the arguments object)

看如下递归例子

 
function factorial(num){
    if (num <= 1) {
        return 1;
    } else {
        return num * factorial(num-1)
    }
}

当函数指针赋值给另一个变量时,上面函数内部与名字factorial耦合则会出现问题 为了解决这个问题,可以用如下方式

function factorial(num){
    if (num <= 1) {
        return 1;
    } else {
        return num * arguments.callee(num-1)
    }
}

var trueFactorial = factorial;
                   
factorial = function(){
    return 0;
};
                   
alert(trueFactorial(5)); //120
alert(factorial(5)); //0

this

某个全局环境中的函数,它的this指针等同于window 某个对象内部的函数的this,它指向这个对象本身。

如下:

window.color = "red";
var o = { color: "blue" };
                   
function sayColor(){
    alert(this.color);
}
                   
sayColor(); //"red"
                   
o.sayColor = sayColor;
o.sayColor(); //"blue"

ECMAScript还规范化了另一个函数对象的属性:caller。它指向调用当前函数的函数的引用,如果当前函数时在全局作用域中, 则它的caller属性为null。如下代码:

function outer(){
    inner();
}
function inner(){
 alert(inner.caller);
}
outer();

上述代码将在警告框显示outer()函数的源代码 为了实现更松散的耦合,可以如下

function outer(){
    inner();
}
function inner(){
    alert(arguments.callee.caller);
}
outer();

Function Properties and Methods

Each function has two properties: length and prototype .length属性返回函数的参数个数,prototype不在这里讲解。

function sayName(name){
    alert(name);
} 
                   
function sum(num1, num2){
    return num1 + num2;
}
                   
function sayHi(){
 alert("hi");
}
                   
alert(sayName.length); //1
alert(sum.length); //2
alert(sayHi.length); //0

There are two additional methods for functions: apply() and call().

apply(),call()的基本作用都是调用函数,如下

function sum(num1, num2){
    return num1 + num2;
}
                   
function callSum1(num1, num2){
    return sum.apply(this, arguments); //passing in arguments object
}
                   
function callSum2(num1, num2){
    return sum.apply(this, [num1, num2]); //passing in array
}
                   
alert(callSum1(10,10)); //20
alert(callSum2(10,10)); //20

function sum(num1, num2){
    return num1 + num2;
}
  

call()方法需要罗列出具体每个参数,apply()方法只需使用arguments。

function callSum(num1, num2){
    return sum.call(this, num1, num2);
}
                   
alert(callSum(10,10)); //20

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); //blue

Running sayColor.call(o) switches the context of the function such that this points to o, resulting in a display of “blue”.

使用apply() , call() 扩大函数的作用域的好处是,对象与方法不需要有任何耦合关系。

ECMAScript 5 定义了一个bind()方法,The bind() method creates a new function instance whose this value is bound to the value that was passed into bind()

如下

window.color = "red";
var o = { color: "blue" };
                   
function sayColor(){
    alert(this.color);
}
var objectSayColor = sayColor.bind(o);
objectSayColor(); //blue

另外

函数对象调用toLocaleString() toString() valueOf() 将返回函数代码