JavaScript创建对象_动力节点Java学院整理


Posted in Javascript onJune 27, 2017

JavaScript对每个创建的对象都会设置一个原型,指向它的原型对象。

当我们用obj.xxx访问一个对象的属性时,JavaScript引擎先在当前对象上查找该属性,如果没有找到,就到其原型对象上找,如果还没有找到,就一直上溯到Object.prototype对象,最后,如果还没有找到,就只能返回undefined。
例如,创建一个Array对象:

var arr = [1, 2, 3];

其原型链是:

arr ----> Array.prototype ----> Object.prototype ----> null

Array.prototype定义了indexOf()、shift()等方法,因此你可以在所有的Array对象上直接调用这些方法。
当我们创建一个函数时:

function foo() {
 return 0;
}

函数也是一个对象,它的原型链是:

foo ----> Function.prototype ----> Object.prototype ----> null

由于Function.prototype定义了apply()等方法,因此,所有函数都可以调用apply()方法。

很容易想到,如果原型链很长,那么访问一个对象的属性就会因为花更多的时间查找而变得更慢,因此要注意不要把原型链搞得太长。

构造函数

除了直接用{ ... }创建一个对象外,JavaScript还可以用一种构造函数的方法来创建对象。它的用法是,先定义一个构造函数:

function Student(name) {
 this.name = name;
 this.hello = function () {
  alert('Hello, ' + this.name + '!');
 }
}

你会问,咦,这不是一个普通函数吗?

这确实是一个普通函数,但是在JavaScript中,可以用关键字new来调用这个函数,并返回一个对象:

var xiaoming = new Student('小明');
xiaoming.name; // '小明'
xiaoming.hello(); // Hello, 小明!

注意,如果不写new,这就是一个普通函数,它返回undefined。但是,如果写了new,它就变成了一个构造函数,它绑定的this指向新创建的对象,并默认返回this,也就是说,不需要在最后写return this;。

新创建的xiaoming的原型链是:

xiaoming ----> Student.prototype ----> Object.prototype ----> null

也就是说,xiaoming的原型指向函数Student的原型。如果你又创建了xiaohong、xiaojun,那么这些对象的原型与xiaoming是一样的:
xiaoming ?
xiaohong -→ Student.prototype ----> Object.prototype ----> null
xiaojun  ?

用new Student()创建的对象还从原型上获得了一个constructor属性,它指向函数Student本身:

xiaoming.constructor === Student.prototype.constructor; // true
Student.prototype.constructor === Student; // true

Object.getPrototypeOf(xiaoming) === Student.prototype; // true

xiaoming instanceof Student; // true

看晕了吧?用一张图来表示这些乱七八糟的关系就是:

JavaScript创建对象_动力节点Java学院整理

红色箭头是原型链。注意,Student.prototype指向的对象就是xiaoming、xiaohong的原型对象,这个原型对象自己还有个属性constructor,指向Student函数本身。
另外,函数Student恰好有个属性prototype指向xiaoming、xiaohong的原型对象,但是xiaoming、xiaohong这些对象可没有prototype这个属性,不过可以用__proto__这个非标准用法来查看。
现在我们就认为xiaoming、xiaohong这些对象“继承”自Student。
不过还有一个小问题,注意观察:

xiaoming.name; // '小明'
xiaohong.name; // '小红'
xiaoming.hello; // function: Student.hello()
xiaohong.hello; // function: Student.hello()
xiaoming.hello === xiaohong.hello; // false

xiaoming和xiaohong各自的name不同,这是对的,否则我们无法区分谁是谁了。
xiaoming和xiaohong各自的hello是一个函数,但它们是两个不同的函数,虽然函数名称和代码都是相同的!
如果我们通过new Student()创建了很多对象,这些对象的hello函数实际上只需要共享同一个函数就可以了,这样可以节省很多内存。

要让创建的对象共享一个hello函数,根据对象的属性查找原则,我们只要把hello函数移动到xiaoming、xiaohong这些对象共同的原型上就可以了,也就是Student.prototype:

JavaScript创建对象_动力节点Java学院整理

修改代码如下:

function Student(name) {
 this.name = name;
}

Student.prototype.hello = function () {
 alert('Hello, ' + this.name + '!');
};

用new创建基于原型的JavaScript的对象就是这么简单!

忘记写new怎么办

如果一个函数被定义为用于创建对象的构造函数,但是调用时忘记了写new怎么办?
在strict模式下,this.name = name将报错,因为this绑定为undefined,在非strict模式下,this.name = name不报错,因为this绑定为window,于是无意间创建了全局变量name,并且返回undefined,这个结果更糟糕。
所以,调用构造函数千万不要忘记写new。为了区分普通函数和构造函数,按照约定,构造函数首字母应当大写,而普通函数首字母应当小写,这样,一些语法检查工具如jslint将可以帮你检测到漏写的new。
最后,我们还可以编写一个createStudent()函数,在内部封装所有的new操作。一个常用的编程模式像这样:

function Student(props) {
 this.name = props.name || '匿名'; // 默认值为'匿名'
 this.grade = props.grade || 1; // 默认值为1
}

Student.prototype.hello = function () {
 alert('Hello, ' + this.name + '!');
};

function createStudent(props) {
 return new Student(props || {})
}

这个createStudent()函数有几个巨大的优点:一是不需要new来调用,二是参数非常灵活,可以不传,也可以这么传:

var xiaoming = createStudent({
 name: '小明'
});

xiaoming.grade; // 1

如果创建的对象有很多属性,我们只需要传递需要的某些属性,剩下的属性可以用默认值。由于参数是一个Object,我们无需记忆参数的顺序。如果恰好从JSON拿到了一个对象,就可以直接创建出xiaoming。

练习

请利用构造函数定义Cat,并让所有的Cat对象有一个name属性,并共享一个方法say(),返回字符串'Hello, xxx!':

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js正确获取元素样式详解
Aug 07 Javascript
js parentElement和offsetParent之间的区别
Mar 23 Javascript
node.js适合游戏后台开发吗?
Sep 03 Javascript
基于BootStrap Metronic开发框架经验小结【八】框架功能总体界面介绍
May 12 Javascript
js 自带的 map() 方法全面了解
Aug 16 Javascript
AngularJs bootstrap搭载前台框架——js控制部分
Sep 01 Javascript
纯js实现html转pdf的简单实例(推荐)
Feb 16 Javascript
深入理解Angular中的依赖注入
Jun 26 Javascript
webpack vue 项目打包生成的文件,资源文件报404问题的修复方法(总结篇)
Jan 09 Javascript
微信小程序自定义纯净模态框(弹出框)的实例代码
Mar 09 Javascript
基于JQuery实现页面定时弹出广告
May 08 jQuery
vue-router之解决addRoutes使用遇到的坑
Jul 19 Javascript
JavaScript字符串_动力节点Java学院整理
Jun 27 #Javascript
JavaScript变量作用域_动力节点Java学院整理
Jun 27 #Javascript
详解微信小程序 登录获取unionid
Jun 27 #Javascript
JavaScript定义函数_动力节点Java学院整理
Jun 27 #Javascript
详解vue项目构建与实战
Jun 27 #Javascript
微信小程序 蓝牙的实现实例代码
Jun 27 #Javascript
微信小程序 开发MAP(地图)实例详解
Jun 27 #Javascript
You might like
PHP启动windows应用程序、执行bat批处理、执行cmd命令的方法(exec、system函数详解)
2014/10/20 PHP
简单了解PHP编程中数组的指针的使用
2015/11/30 PHP
Laravel Intervention/image图片处理扩展包的安装、使用与可能遇到的坑详解
2017/11/14 PHP
基于jquery跨浏览器显示的file上传控件
2011/10/24 Javascript
javascript 进阶篇3 Ajax 、JSON、 Prototype介绍
2012/03/14 Javascript
HTML中的setCapture和releaseCapture使用介绍
2012/03/21 Javascript
使用jquery操作session方法分享
2015/01/22 Javascript
AngularJS  $on、$emit和$broadcast的使用
2016/09/05 Javascript
关于JavaScript和jQuery的类型判断详解
2016/10/08 Javascript
详解Angular路由之路由守卫
2018/05/10 Javascript
微信小程序实现红包雨功能
2018/07/11 Javascript
vue动态删除从数据库倒入列表的某一条方法
2018/09/29 Javascript
基于ssm框架实现layui分页效果
2019/07/27 Javascript
vue组件传值的实现方式小结【三种方式】
2020/02/05 Javascript
如何利用node转发请求详解
2020/09/17 Javascript
[48:21]Mski vs VGJ.S Supermajor小组赛C组 BO3 第一场 6.3
2018/06/04 DOTA
[48:26]VGJ.S vs infamous Supermajor 败者组 BO3 第二场 6.4
2018/06/05 DOTA
[54:15]DOTA2-DPC中国联赛 正赛 DLG vs Dragon BO3 第二场2月1日
2021/03/11 DOTA
Python中的zip函数使用示例
2015/01/29 Python
对pandas replace函数的使用方法小结
2018/05/18 Python
Python自动化之数据驱动让你的脚本简洁10倍【推荐】
2019/06/04 Python
通过实例简单了解Python中yield的作用
2019/12/11 Python
python实现简易版学生成绩管理系统
2020/06/22 Python
Python 数据的累加与统计的示例代码
2020/08/03 Python
施华洛世奇韩国官网:SWAROVSKI韩国
2018/06/05 全球购物
大学班长的职责
2014/01/27 职场文书
党的群众路线教育实践活动宣传方案
2014/02/23 职场文书
干部考核评语
2014/04/29 职场文书
学习朴航瑛老师爱岗敬业先进事迹思想汇报
2014/09/17 职场文书
2015年店长个人工作总结
2015/10/23 职场文书
求职信:求职应该注意的问题
2019/04/24 职场文书
MySQL系列之七 MySQL存储引擎
2021/07/02 MySQL
JavaScript 事件捕获冒泡与捕获详情
2021/11/11 Javascript
vue 自定义组件添加原生事件
2022/04/21 Vue.js
Redis 异步机制
2022/05/15 Redis
javascript进阶篇深拷贝实现的四种方式
2022/07/07 Javascript