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 IE 浏览器判定代码
Mar 21 Javascript
jQuery .tmpl(), .template()学习资料小结
Jul 18 Javascript
用jQuery中的ajax分页实现代码
Sep 20 Javascript
jQuery 快速结束当前正在执行的动画
Nov 20 Javascript
JS实现的最简Table选项卡效果
Oct 14 Javascript
JavaScript继承模式粗探
Jan 12 Javascript
js实现旋转木马效果
Mar 17 Javascript
vue将对象新增的属性添加到检测序列的方法
Feb 24 Javascript
JavaScript面试技巧之数组的一些不low操作
Mar 22 Javascript
基于vue-cli3创建libs库的实现方法
Dec 04 Javascript
vue实现带过渡效果的下拉菜单功能
Feb 19 Javascript
解决angular 使用原生拖拽页面卡顿及表单控件输入延迟问题
Apr 21 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笔记之:数据类型与常量的使用分析
2013/05/14 PHP
深入array multisort排序原理的详解
2013/06/18 PHP
php生成随机颜色的方法
2014/11/13 PHP
使用PHPExcel实现数据批量导出为excel表格的方法(必看)
2017/06/09 PHP
Laravel使用支付宝进行支付的示例代码
2017/08/16 PHP
PHP常量define和const的区别详解
2019/05/18 PHP
javascript 对表格的行和列都能加亮显示
2008/12/26 Javascript
ajax中get和post的说明及使用与区别
2012/12/23 Javascript
JS连接SQL数据库与ACCESS数据库的方法实例
2013/11/21 Javascript
利用jquery写的左右轮播图特效
2014/02/12 Javascript
js 打开新页面在屏幕中间的实现方法
2016/11/02 Javascript
JS仿Base.js实现的继承示例
2017/04/07 Javascript
利用node.js爬取指定排名网站的JS引用库详解
2017/07/25 Javascript
vuejs 单文件组件.vue 文件的使用
2017/07/28 Javascript
基于js 各种排序方法和sort方法的区别(详解)
2018/01/03 Javascript
vue如何自动化打包测试环境和正式环境的dist/test文件
2019/06/06 Javascript
js实现列表向上无限滚动
2020/01/13 Javascript
JavaScript实现tab栏切换效果
2020/03/16 Javascript
django admin添加数据自动记录user到表中的实现方法
2018/01/05 Python
django orm 通过related_name反向查询的方法
2018/12/15 Python
python实现网页自动签到功能
2019/01/21 Python
Python 中包/模块的 `import` 操作代码
2019/04/22 Python
解决python DataFrame 打印结果不换行问题
2020/04/09 Python
django queryset相加和筛选教程
2020/05/18 Python
python ETL工具 pyetl
2020/06/07 Python
matplotlib 画动态图以及plt.ion()和plt.ioff()的使用详解
2021/01/05 Python
欧洲最大的美妆零售网站:Feelunique
2017/01/14 全球购物
GoDaddy英国:全球排名第一的域名注册商
2018/06/08 全球购物
FragranceNet中文网:北美健康美容线上零售商
2020/08/26 全球购物
经济管理专业毕业生推荐信
2013/11/11 职场文书
低碳生活的宣传标语
2014/06/23 职场文书
做一个有道德的人活动实施方案
2014/08/23 职场文书
2015年小学英语教师工作总结
2015/05/12 职场文书
2015年建筑工程工作总结
2015/05/13 职场文书
Golang 空map和未初始化map的注意事项说明
2021/04/29 Golang
pd.drop_duplicates删除重复行的方法实现
2022/06/16 Python