JavaScript极简入门教程(二):对象和函数


Posted in Javascript onOctober 25, 2014

阅读本文需要有其他语言的编程经验。

JavaScript 中的简单类型包括:

1.数字
2.字符串
3.布尔(true 和 false)
4.null
5.undefined

此外的其他类型均是对象(我们不要被 typeof 操作符的返回值所迷惑),例如:

1.函数
2.数组
3.正则表达式
4.对象(对象自然也是对象)

对象基础

在 JavaScript 中,对象是属性的集合(对象为关联数组),每个属性包括:

1.属性名,必须为字符串
2.属性值,可以为除了 undefined 之外的任何值

通过对象 literal 创建对象:

// 通过对象 literal {} 创建空对象

var empty_object = {};

对象的属性名和属性值:

var stooge = {

    // "first-name" 为属性名,"Jerome" 为属性值

    "first-name": "Jerome",

    // "last-name" 为属性名,"Howard" 为属性值

    "last-name": "Howard"

};

如果属性名是合法的标识符,那么可以省略引号:

var flight = {

    airline: "Oceanic",

    number: 815,

    departure: {

        IATA: "SYD",

        time: "2004-09-22 14:55",

        city: "Sydney"

    },

    arrival: {

        IATA: "LAX",

        time: "2004-09-23 10:42",

        city: "Los Angeles"

    }

};

我们看一下属性访问的例子:

var owner = { name: "Name5566" };

 

owner.name; // "Name5566"

owner["name"]; // "Name5566"

 

owner.job; // undefined

owner.job = "coder"; // 或者 owner["job"] = "coder";

如果属性名不是合法标识符,那么需要用引号包裹。不存在的属性值为 undefined。对象是通过引用而非按值传递:

var x = {};

var owner = x;

owner.name = "Name5566";

x.name; // x.name === "Name5566"

这里 x 和 owner 引用同一个对象。

对象的属性可以使用 delete 操作符删除:

delete obj.x; // 删除对象 obj 的 x 属性

对象的原型(prototype)

每一个对象都被链接了一个原型对象(prototype object),对象能够从原型对象中继承属性。我们通过对象 literal 创建一个对象,它的原型对象为 Object.prototype 对象(Object.prototype 对象本身没有原型对象)。我们在创建对象的时候,可以设置对象的原型对象(之后再讨论具体的设置方法)。在尝试获取(而非修改)对象的某个属性时,如果该对象不存在此属性,那么 JavaScript 会尝试从此对象的原型对象中获取此属性,如果原型对象中没有该属性,那么再从此原型对象的原型对象中查找,以此类推,直到 Object.prototype 原型对象。相比获取属性而言,我们修改对象的某个属性时,不会影响原型对象。

函数基础

在 JavaScript 中函数也是对象,其链接到 Function.prototype 原型对象(Function.prototype 链接到 Object.prototype)。函数存在一个名为 prototype 的属性,其值的类型为对象,此对象存在一个属性 constructor,constructor 的值为此函数:

var f = function() {}

 

typeof f.prototype; // 'object'

typeof f.prototype.constructor; // 'function'

 

f === f.prototype.constructor; // true

函数是对象,你可以像使用对象一样使用函数,也就是说,函数可以保存在变量、数组中,可以作为参数传递给函数,函数内部可以定义函数。附带提及一下,函数有两个被隐藏的属性:

1.函数的上下文
2.函数的代码

函数的创建如下:

var f = function add(a, b) {

    return a + b;

}

 

console.log(f); // 输出 [Function: add]

关键字 function 后的函数名是可选的,我们制定函数名主要出于几个目的:

1.为了递归调用
2.被调试器、开发工具等用来标识函数

很多时候我们并不需要函数名,没有函数名的函数被叫做匿名函数。有括号包裹的为参数列表。JavaScript 不要求实参和形参匹配,例如:

var add = function(a, b) {

    return a + b;

}

 

add(1, 2, 3); // 实参和形参不匹配

如果实参过多,那么多余的实参会被忽略,如果实参过少,那么未被赋值的形参的值为 undefined。函数一定有一个返回值,如果没有通过 return 语句指定返回值,那么函数返回值为 undefined。

一个函数和其访问的外部变量组成一个闭包。这是 JavaScript 的关键魅力所在。

函数调用

每个函数被调用时,会接收到两个额外的参数:

1.this
2.arguments

this 的值和具体调用的模式有关,在 JavaScript 中有四种调用模式:

1.方法调用模式。对象的属性如果是函数,则称其为方法。如果一个方法通过 o.m(args) 被调用,this 为对象 o(由此可见,在调用时,this 和 o 才进行绑定),例如:

var obj = {

    value: 0,

    increment: function(v) {

        this.value += (typeof v === 'number' ? v : 1);

    }

};

obj.increment(); // this === obj

2.函数调用模式。如果一个函数不是一个对象的属性,那么它将作为一个函数被调用,这时候 this 被绑定到全局对象上,例如:

message = 'Hello World';

var p = function() {

 console.log(this.message);

}

 

p(); // 输出 'Hello World'

这种行为有时候让人疑惑,看一个例子:

obj = {

    value: 0,

    increment: function() {

        var helper = function() {

            // 对全局对象中的 value 加 1

            this.value += 1;

        }

 

        // helper 被作为一个函数来调用

        // 因此 this 为全局对象

        helper();

    }

};

 

obj.increment(); // obj.value === 0

我们期望的结果应该是:

obj = {

    value: 0,

    increment: function() {

        var that = this;

        var helper = function() {

            that.value += 1;

        }

 

        helper();

    }

};

 

obj.increment(); // obj.value === 1

3.构造函数调用模式。意图使用 new 前缀的函数被叫做构造函数,例如:

// Test 被叫做构造函数

var Test = function(string) {

    this.message = string;

}

 

var myTest = new Test("Hello World");

一个函数前面可以加上 new 来调用(这样的函数通常大写开头),加上 new 之后将创建一个链接到此函数的 prototype 属性的对象,且构造函数中 this 为此对象。

4.apply 调用模式。函数的 apply 方法被用于调用函数,其有两个参数,第一个为 this,第二个为参数数组,例如:

var add = function(a, b) {

    return a + b;

}

 

var ret = add.apply(null, [3, 4]); // ret === 7

函数调用时,我们能够访问一个名为 arguments 的类数组(非真正的 JavaScript 数组),其包含了所有的实参,这样我们就能实现变长参数:

var add = function() {

    var sum = 0;

    for (var i=0; i<arguments.length; ++i) {

        sum += arguments[i];

    }

    return sum;

}

 

add(1, 2, 3, 4);

异常

现在来说说 JavaScript 的异常处理机制。我们使用 throw 语句来抛出异常,try-cache 语句来捕获并处理异常:

var add = function (a, b) {

    if (typeof a !== 'number' || typeof b !== 'number') {

        // 抛出异常

        throw {

            name: 'TypeError',

            message: 'add needs numbers'

        };

    }

    return a + b;

}

 

// 捕获并处理异常

try {

    add("seven");

// e 为抛出的异常对象

} catch (e) {

    console.log(e.name + ': ' + e.message);

}

为JavaScript 类型添加属性

JavaScript 中大多数类型存在构造函数:

1.对象的构造函数为 Object
2.数组的构造函数为 Array
3.函数的构造函数为 Function
4.字符串的构造函数为 String
5.数字的构造函数为 Number
6.布尔的构造函数为 Boolean
7.正则表达式的构造函数为 RegExp

我们可以向构造函数的 prototype 添加属性(常添加方法),使得此属性对相关变量可用:

Number.prototype.integer = function() {

    return Math[this < 0 ? 'ceil' : 'floor'](this);

}

 

(1.1).integer(); // 1

作用域

JavaScript 需要通过函数来构建作用域:

function() {

    // ...

}();

这里创建并执行了一个匿名函数。通过作用域能够隐藏不希望暴露的变量:

var obj = function() {

    // 隐藏 value,外部无法访问

    var value = 0;

 

    return {

        // 仅此方法可以修改 value

        increment: function() {

            value += 1;

        },

        // 仅此方法可以读取 value

        getValue: function() {

            return value;

        }

    };

}();

 

obj.increment();

obj.getValue() === 1;

继承

JavaScript 实现继承的方式很多。
在创建对象时,我们可以设置对象关联的原型对象,我们这样做:

// 创建一个对象 o,其原型对象为 {x:1, y:2}

var o = Object.create({x:1, y:2});

Object.create 方法被定义在 ECMAScript 5 中,如果你使用 ECMAScript 3 时可以自己实现一个 create 方法:

// 如果未定义 Object.create 方法

if (typeof Object.create !== 'function') {

    // 创建 Object.create 方法

    Object.create = function (o) {

        var F = function () {};

        F.prototype = o;

        // 创建一个新对象,此对象的原型对象为 o

        return new F();

    };

}

通过 Object.create 方法我们进行基于原型继承:一个新对象直接继承一个旧对象的属性(相对于基于类的继承,这里无需类的存在,对象直接继承对象)。范例:

var myMammal = {

    name: 'Herb the Mammal',

    get_name: function() {

        return this.name;

    },

    says: function() {

        return this.saying || '';

    }

};

 

// 继承 myMammal

var myCat = Object.create(myMammal);

myCat.name = 'Henrietta';

myCat.saying = 'meow';

myCat.purr = function(n) {

    var i, s = '';

    for (i = 0; i < n; i += 1) {

        if (s) {

            s += '-';

        }

        s += 'r';

    }

    return s;

};

myCat.get_name = function() {

    return this.says() + ' ' + this.name + ' ' + this.says();

};

上面的代码很简单,但是没法保护私有成员。我们可以使用模块模式。在模块模式中,某类对象由一个函数产生,并利用函数作用域保护私有成员不被外部访问:

// mammal 函数,用于构造 mammal 对象

var mammal = function(spec) {

    // that 为构造的对象

    var that = {};

 

    // 公有方法 get_name 可被外部访问

    that.get_name = function() {

        // spec.name 外部无法直接访问

        return spec.name;

    };

 

    // 公有方法 says 可被外部访问

    that.says = function() {

        // spec.saying 外部无法直接访问

        return spec.saying || '';

    };

 

    return that;

};

 

// 创建 mammal 对象

var myMammal = mammal({name: 'Herb'});

 

// cat 函数,用于构造 cat 对象

var cat = function(spec) {

    spec.saying = spec.saying || 'meow';

 

    // cat 继承自 mammal,因此先构造出 mammal 对象

    var that = mammal(spec);

 

    // 添加公有方法 purr

    that.purr = function(n) {

        var i, s = '';

        for (i = 0; i < n; i += 1) {

            if (s) {

                s += '-';

            }

            s += 'r';

        }

        return s;

    };

 

    // 修改公有方法 get_name

    that.get_name = function() {

        return that.says() + ' ' + spec.name +

            ' ' + that.says();

        return that;

    };

};

 

// 创建 cat 对象

var myCat = cat({name: 'Henrietta'});

在模块模式中,继承是通过调用构造函数来实现的。另外,我们还可以在子类中访问父类的方法:

Object.prototype.superior = function(name) {

    var that = this, method = that[name];

    return function() {

        return method.apply(that, arguments);

    };

};

 

var coolcat = function (spec) {

    // 获取子类的 get_name 方法

    var that = cat(spec), super_get_name = that.superior('get_name');

    that.get_name = function(n) {

        return 'like ' + super_get_name() + ' baby';

    };

    return that;

};

Javascript 相关文章推荐
Javascript attachEvent传递参数的办法
Dec 14 Javascript
编写自己的jQuery插件简单实现代码
Apr 19 Javascript
js 对小数加法精度处理示例说明
Dec 27 Javascript
Jquery中find与each方法用法实例
Feb 04 Javascript
Bootstrap每天必学之栅格系统(布局)
Nov 25 Javascript
6种javascript显示当前系统时间代码
Dec 01 Javascript
JS判断字符串字节数并截取长度的方法
Mar 05 Javascript
【JS+CSS3】实现带预览图幻灯片效果的示例代码
Mar 17 Javascript
JS实现数组去重复值的方法示例
Feb 18 Javascript
微信小程序中form 表单提交和取值实例详解
Apr 20 Javascript
快速将Vue项目升级到webpack3的方法步骤
Sep 14 Javascript
在angularJs中进行数据遍历的2种方法
Oct 08 Javascript
JavaScript极简入门教程(一):基础篇
Oct 25 #Javascript
上传图片预览JS脚本 Input file图片预览的实现示例
Oct 23 #Javascript
实用框架(iframe)操作代码
Oct 23 #Javascript
form.submit()不能提交表单的原因分析
Oct 23 #Javascript
Google Maps API地图应用示例分享
Oct 23 #Javascript
深入分析JQuery和JavaScript的异同
Oct 23 #Javascript
jquery实现导航固定顶部的效果仿蘑菇街
Oct 22 #Javascript
You might like
一步一步学习PHP(2)――PHP类型
2010/02/15 PHP
优化php效率,提高php性能的一些方法
2011/03/24 PHP
PHP生成随机密码方法汇总
2015/08/27 PHP
thinkPHP中create方法与令牌验证实例浅析
2015/12/08 PHP
php 判断字符串编码是utf-8 或gb2312实例
2016/11/01 PHP
PHP实现简单注册登录系统
2020/12/28 PHP
setInterval 和 setTimeout会产生内存溢出
2008/02/15 Javascript
JavaScript 联动的无限级封装类,数据采用非Ajax方式,随意添加联动
2010/06/29 Javascript
6个DIV 135或246间隔一秒轮番显示效果
2010/07/24 Javascript
利用google提供的API(JavaScript接口)获取网站访问者IP地理位置的代码详解
2010/07/24 Javascript
Jvascript学习实践案例(开发常用)
2012/06/25 Javascript
JS特权方法定义作用以及与公有方法的区别
2013/03/18 Javascript
Extjs 4.x 得到form CheckBox 复选框的值
2014/05/04 Javascript
jQuery中removeAttr()方法用法实例
2015/01/05 Javascript
javascript 兼容各个浏览器的事件
2015/02/04 Javascript
jQuery实现伸展与合拢panel的方法
2015/04/30 Javascript
微信js-sdk预览图片接口及从拍照或手机相册中选图接口用法示例
2016/10/13 Javascript
基于iScroll实现内容滚动效果
2018/03/21 Javascript
vue debug 二种方法
2018/09/16 Javascript
python实现分析apache和nginx日志文件并输出访客ip列表的方法
2015/04/04 Python
python实现在每个独立进程中运行一个函数的方法
2015/04/23 Python
python 字典中取值的两种方法小结
2018/08/02 Python
Python json模块dumps、loads操作示例
2018/09/06 Python
Python Pandas批量读取csv文件到dataframe的方法
2018/10/08 Python
详解python算法之冒泡排序
2019/03/05 Python
基于Python新建用户并产生随机密码过程解析
2019/10/08 Python
如何对python的字典进行排序
2020/06/19 Python
Python常用库Numpy进行矩阵运算详解
2020/07/21 Python
流行文化收藏品:Sideshow(DC漫画,星球大战,漫威)
2019/03/17 全球购物
Tessabit美国:集世界奢侈品和设计师品牌的意大利精品买手店
2020/06/29 全球购物
会计系毕业个人自荐信格式
2013/09/23 职场文书
中学生爱国演讲稿
2013/12/31 职场文书
培训自我鉴定
2014/01/31 职场文书
《湘夫人》教学反思
2014/02/21 职场文书
初中毕业生的自我评价
2014/03/03 职场文书
国庆65周年演讲稿:回首往昔,展望未来
2014/09/21 职场文书