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 相关文章推荐
Js 回车换行处理的办法及replace方法应用
Jan 24 Javascript
jquery缓动swing liner控制动画过程不同时刻的速度
May 29 Javascript
Javascript中的五种数据类型详解
Dec 26 Javascript
jQuery解决input元素的blur事件和其他非表单元素的click事件冲突问题
Aug 15 Javascript
Angular ng-class详解及实例代码
Sep 19 Javascript
最常见的左侧分类菜单栏jQuery实现代码
Nov 28 Javascript
jquery获取transform里的值实现方法
Dec 12 jQuery
vue elementUI tree树形控件获取父节点ID的实例
Sep 12 Javascript
使用gulp构建前端自动化的方法示例
Dec 25 Javascript
基于Vue实现电商SKU组合算法问题
May 29 Javascript
Vue 前端实现登陆拦截及axios 拦截器的使用
Jul 17 Javascript
在Layui 的表格模板中,实现layer父页面和子页面传值交互的方法
Sep 10 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
Discuz 模板引擎的封装类代码
2008/07/18 PHP
在Windows系统上安装PHP运行环境文字教程
2010/07/19 PHP
基于php权限分配的实现代码
2013/04/28 PHP
php设置允许大文件上传示例代码
2014/03/10 PHP
PHP设置进度条的方法
2015/07/08 PHP
总结PHP如何获取当前主机、域名、网址、路径、端口和参数等
2016/09/09 PHP
PHP经典实用正则表达式小结
2017/05/04 PHP
jQuery源码分析-04 选择器-Sizzle-工作原理分析
2011/11/14 Javascript
javascript window.confirm确认 取消对话框实现代码小结
2012/10/21 Javascript
浅析js封装和作用域
2013/07/09 Javascript
解决jquery操作checkbox火狐下第二次无法勾选问题
2014/02/10 Javascript
JQuery弹出层示例可自定义
2014/05/19 Javascript
jquery读取xml文件实现省市县三级联动的方法
2015/05/29 Javascript
基于jquery实现在线选座订座之影院篇
2015/08/24 Javascript
详解JavaScript UTC时间转换方法
2016/01/07 Javascript
学习JavaScript设计模式之状态模式
2016/01/08 Javascript
浅谈js构造函数的方法与原型prototype
2016/07/04 Javascript
AngularJS $injector 依赖注入详解
2016/09/14 Javascript
JS判断Android、iOS或浏览器的多种方法(四种方法)
2017/06/29 Javascript
表格展示利器 Bootstrap Table实例代码
2017/09/06 Javascript
详解微信小程序与内嵌网页交互实现支付功能
2018/10/22 Javascript
js继承的这6种方式!(上)
2019/04/23 Javascript
解决vue更新路由router-view复用组件内容不刷新的问题
2019/11/04 Javascript
[58:11]守擂赛第二周擂主赛 DeMonsTer vs Leopard
2020/04/28 DOTA
Python 生成一个从0到n个数字的列表4种方法小结
2019/11/28 Python
opencv+python实现鼠标点击图像,输出该点的RGB和HSV值
2020/06/02 Python
浅谈matplotlib中FigureCanvasXAgg的用法
2020/06/16 Python
浅谈sklearn中predict与predict_proba区别
2020/06/28 Python
处理HTML5新标签的浏览器兼容版问题
2017/03/13 HTML / CSS
受外贸欢迎的美国主机:BlueHost
2017/05/16 全球购物
护理专业的自荐信
2013/10/22 职场文书
民事起诉状范文
2015/05/19 职场文书
学会用Python实现滑雪小游戏,再也不用去北海道啦
2021/05/20 Python
vue-element-admin项目导入和导出的实现
2021/05/21 Vue.js
pandas数值排序的实现实例
2021/07/25 Python
mysql中数据库覆盖导入的几种方式总结
2022/03/25 MySQL