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 日期验证正则附asp日期格式化函数
Sep 11 Javascript
JQuery从头学起第三讲
Jul 06 Javascript
基于jQuery实现的图片切换焦点图整理
Dec 07 Javascript
js实现带缓冲效果的仿QQ面板折叠菜单代码
Sep 06 Javascript
Windows下用PyCharm和Visual Studio开始Python编程
Oct 26 Javascript
javascript经典特效分享 手风琴、轮播图、图片滑动
Sep 14 Javascript
javascript实现图片左右滚动效果【可自动滚动,有左右按钮】
Sep 19 Javascript
cocos creator Touch事件应用(触控选择多个子节点的实例)
Sep 10 Javascript
不到200行 JavaScript 代码实现富文本编辑器的方法
Jan 03 Javascript
简单的React SSR服务器渲染实现
Dec 11 Javascript
序列化模块json代码实例详解
Mar 03 Javascript
js实现盒子移动动画效果
Aug 09 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 调用远程url的六种方法小结
2009/11/02 PHP
php 处理上百万条的数据库如何提高处理查询速度
2010/02/08 PHP
php将access数据库转换到mysql数据库的方法
2014/12/24 PHP
PHP转盘抽奖接口实例
2015/02/09 PHP
PHP实现简单汉字验证码
2015/07/28 PHP
PHP实现自动识别原编码并对字符串进行编码转换的方法
2016/07/13 PHP
PHP实现的基于单向链表解决约瑟夫环问题示例
2017/09/30 PHP
如何在centos8自定义目录安装php7.3
2019/11/28 PHP
js removeChild 障眼法 可能出现的错误
2009/10/06 Javascript
jquery formValidator插件ajax验证 内容不做任何修改再离开提示错误的bug解决方法
2013/01/04 Javascript
jQuery ajax serialize()方法的使用以及常见问题解决
2013/01/27 Javascript
JavaScript获取flash对象与网上的有所不同
2014/04/21 Javascript
jQuery中removeData()方法用法实例
2014/12/27 Javascript
JavaScript中的alert()函数使用技巧详解
2014/12/29 Javascript
JavaScript中的比较操作符&gt;、=、
2014/12/31 Javascript
JS button按钮实现submit按钮提交效果
2016/11/01 Javascript
移动开发之自适应手机屏幕宽度
2016/11/23 Javascript
jQuery操作复选框(CheckBox)的取值赋值实现代码
2017/01/10 Javascript
JavaScrpt判断一个数是否是质数的实例代码
2017/06/11 Javascript
Vue.js实现微信过渡动画左右切换效果
2017/06/13 Javascript
bootstrap模态框弹出和隐藏,动态改变中间内容的实例
2018/08/10 Javascript
JS实现数组的增删改查操作示例
2018/08/29 Javascript
vue-cli 使用axios的操作方法及整合axios的多种方法
2018/09/12 Javascript
详解mpvue中小程序自定义导航组件开发指南
2019/02/11 Javascript
[01:55]2014DOTA2国际邀请赛 BBC正赛第一天总结
2014/07/10 DOTA
Python爬取网易云音乐热门评论
2017/03/31 Python
Python爬取破解无线网络wifi密码过程解析
2019/09/17 Python
Python TestSuite生成测试报告过程解析
2020/07/23 Python
html5需遵循的6个设计原则
2016/04/27 HTML / CSS
YesStyle美国/全球:购买亚洲时装、美容化妆品和生活百货
2017/01/16 全球购物
土耳其新趋势女装购物网站:Addax
2020/01/07 全球购物
面包店的创业计划书范文
2014/01/16 职场文书
五心教育心得体会
2014/09/04 职场文书
世界水日宣传活动总结
2015/02/09 职场文书
回复函范文
2015/07/14 职场文书
解决Jenkins集成SonarQube遇到的报错问题
2021/07/15 Java/Android