JS对象与原型
一. JS的对象
1.1 创建对象的几种方式
1.1.1 通过字面量创建对象
在js中,一对{}
其实就是一个对象
var person = {
name: "tom",
age: 23,
read: function () {
console.log(name, ": read book")
}
}
1.1.2 通过系统的构造函数
通过系统的构造函数创建一个空的对象,然后用js动态语言的特性,如果一个对象没有某个属性或者方法,那么我们点一下再附上值就好了
var person2 = new Object()
person2.name = "jerry"
person2.age = 23
person2.say = function () {
console.log(person2.name, ": say hello")
}
1.1.3 通过自定义构造方法
自定义构造方法一般都是首字母大写的函数
function Person(name, age, sex) {
this.name = name
this.age = age
this.sex = sex
this.say = function () {
console.log(this.name, " :say hello")
}
}
// 创建对象时,使用 new 关键字
p = new Person("tom", 23, "man")
console.log(p instanceof Person)
自定义的构造方法创建对象,会经历如下几个步骤
- 开辟空间
- 将this设置成当前对象
- 初始化属性和方法
- 将this返回
1.1.4 工厂模式创建对象
function Person(name,age,sex) {
// new Object 作为当前的返回值
var obj = new Object()
obj.name = name
obj.age = age
obj.sex = sex
obj.say = function () {
console.log(this.name," :say hello")
}
// 手动将对象返回出去
return obj
}
// 工厂模式创建对象,不需要使用new 关键字
var p = Person("tom",23,"man")
console.log(p instanceof Person) // false
1.2 构造函数与实例对象
看下面的例子:
// 构造函数和实例的关系
function Person(name) {
this.name = name
this.say = function () {
console.log(this.name," :say hello")
}
}
// 对象p是通过 自定义的构造函数Person创建出来的
var p = new Person("tom")
console.dir(p)
console.dir(Person)
打印的结果如下:
- 实例对象的
__proto__
属性中有constructor属性,上面记录着自己的构造方法。 - Person是构造方法,也是对象,我们直接打印Person得到的结果中有个属性prototype,它里面也有个属性叫做 constructor。里面记录着构造方法就是自己本身。
- 结合上面的例子,我们其实可以得到这样的推断,实例对象的原型属性 和 构造函数的原型属性中的constructor都指向了同一个构造方法 ,然后可以进一步推断
p是Person类型
。
__prototype__实际上就是原型对象,在下文中会详细的说
还是上面的例子,看如下的输出也就能理解了
console.log(p.constructor === Person) // true
console.log(p.__proto__.constructor == Person) // true
console.log(p.__proto__.constructor == Person.prototype.constructor) // true
// 由此推断出,p === Person
console.log(p instanceof Person) // true
其实有个小问题,看上面代码的第一行console.log(p.constructor === Person)
我们通过上面的代码也看不到实例对象p constructor属性啊,怎么就能用,也不报错undefined呢?
其实这就是牵扯到js对象的原型链了,(下面的章节会说),总的来说,就是js的对象会优先使用构造方法中的属性和方法,如果构造函数中不存在我们使用的属性和方法的话,就尝试去这个对象所对应的构造方法中的原型对象中的属性和方法,再没有就会报错。
二. JS的原型
2.1 引入原型的必要性
为什么会突然再来看js的原型呢?
因为看到了vue的源码中,大量的方法都被添加再vm的原型上,所以,回顾一下原型肯定是躲不过去了。
一般我们使用原型就是为了节省空间。
想理解节省了什么空间? 那就看看下面这个不节省空间的例子。
// 构造函数创建对象带来的问题
function Person(name) {
this.name = name
this.say = function () {
console.log(this.name,": say hello")
}
}
var p1 = new Person("tom")
var p2 = new Person("jerry")
p1.say() // tom : say hello
p2.say() // jerry : say hello
// todo 返回false, 表示说,p1和p2的say方法,并不是同一份, 其实这并不是一件好事
console.log(p1.say == p2.say)
上面的p1 和 p2 都是通过一个构造函数创建出来的不同对象,他们里面都有say这个函数,当我们输出 p1.say == p2.say
时,返回了false,说明每个对象中都有一份say方法,那假设有1000个对象,岂不是就有1000个say方法了? 这肯定是浪费空间的。
那么有没有办法可以让每次new出来的对象都使用一份say方法呢?
当然,如下:
// 共享函数,引出原型
function Say() {
console.log(this.name, ": say hellp")
}
function Person(name) {
this.name = name
this.say = Say
}
var p1 = new Person("tom")
var p2 = new Person("jerry")
p1.say()// tom : say hellp
p2.say()// jerry : say hellp
// 这样的话,确实能实现节省空间,但是容易出问题
console.log(p1.say == p2.say) // ture
现在确实实现了我们的需求,但是不够优雅,而且统一出现问题,js是动态类型的语言,那我们像下面这样,假设不知道已经有Say这个函数了,然后将var Say = "hello"
放置在第Say函数之后,就会产生覆盖。
2.2 认识原型
看下的例子:我们往构造方法的原型对象上添加一个say方法。
其实这块也不是不好理解,你想啊,js的对象通过构造方法创建出来,我们把公共的方法,属性放在构造方法的原型对象中,是不是就可以让他们共享这些方法和属性呢?
function Person(name) {
this.name = name
}
// 在原型上添加方法
// 为什么可以说原型是对象呢? 想想js中一个对象可以通过 点 , 动态点添加属性和方法?
Person.prototype.say = function () {
console.log(this.name,":say hello")
}
var p1 = new Person("tom")
var p2 = new Person("jerry")
p1.say()//tom :say hello
p2.say()//jerry :say hello
console.log(p1.say == p2.say) // true
通过console.dir()
打印下上面的实例对象和构造函数,得到如下图:
console.dir(p1)
console.dir(p2)
console.dir(Person)
通过上图可以看到,可以得到下面的结论:
- 实例对象中的直接拥有的标准属性,比如name, 这些都是直接出现在构造方法中的属性,而且这些属性是js对象所私有的。
- 上图中实例对象有个属性叫做:
__proto__
, 这个属性是用来给浏览器使用的,而不是给程序员使用,所以我们称它为非标准属性。 此外谷歌浏览器是支持这个属性的,但是在IE8浏览器中,我们执行这句console.log(p1.__proto__)
会报错,说undefined
2.3 原型,实例对象,构造函数之间到底是什么关系呢?
实例对象是通过 new 构造函数创建出来的,所以构造函数是创建实例对象的模版。
构造函数就是那个首字母大写的函数,在js里面我们能直接
console.log(构造函数)
因为这个构造函数其实也是个对象。原型的作用我们说了,就是为了将公共的方法抽取出来,全部存放在构造函数的原型对象中,而实现数据的共享,节省内存。
实例对象的·
__proto__
, 是个非标准属性,也是个对象,这个对象指向了 构造方法的prototype
属性。构造方法的
prototype
属性是个标准属性, 同时也是个对象,我们对通过构造方法.prototype.属性/方法 = XXX
的方式为其添加属性和方法。我们通过
对象.属性/方法
时, 会优先从对象的构造方法中查找,如果找不到的会再尝试从原型中查找,这也是为什么会出现一个明明没有为一个对象添加相应的属性或者方法但是对象却能点出来,并且能正常使用。当然如果原型中也不存在的话,就会报错说 undefined
2.4 关于this对象
- 看下面的第一个例子
下面出现的this并不难理解, 就是我们new 出来的对象本身
function Person(name) {
// 考虑一下,这个this是谁?
this.name = name
console.log(this)
}
var p = new Person("tom")
- 看下面的第二个例子
我们在构造方法的原型对象上添加一个方法say,在这say方法中使用的this对象指的同样是 我们new 出来的对象本身。即方法的调用者。
function Person(name) {
// 考虑一下,这个this是谁?
this.name = name
console.log("n10: ",this)
}
Person.prototype.say = function () {
// todo 这里的this指的是谁呢?
// 首先,方法是添加在原型对象上, 那么this指的是原型对象吗?
// 通过控制台可以看到,this.name ,其实不是原型对象,而是say()方法的调用者(实例对象)
console.log("n16: ",this.name,": say hello")
}
var p1 = new Person("tom")
var p2 = new Person("jerry")
p1.say()
p2.say()
- 看下面的第三个例子:
下面在给构造方法的原型对象添加方法时,不仅出现了this, 还出现了that。
this对象依然是我们手动new出来的对象本身。
that同样是指向了我们new出来的对象本身,之所以需要中转一下,是因为在按钮的点击事件里面,this指向的是按钮本身。
// 用面向对象的方式封装构造函数
function ChangeStyle(btnId, dvId, color) {
this.btnObj = document.getElementById(btnId)
this.dv = document.getElementById(dvId)
this.color = color
}
// 在构造方法的原型上添加方法
ChangeStyle.prototype.init = function () {
// 这里面的this表示的是 调用init方法的实例对象
var that = this
this.btnObj.onclick = function () {
// todo 为什么原型中的函数中,就不能使用this,而是that呢???
// todo 或者问下,当前函数中的this是谁呢?
that.dv.style.backgroundColor = that.color
}
}
2.5 其他原型的写法
- 最常见的写法就是像下面这样,在当前原型的基础上添加属性或者方法
function Person(name) {
this.name = name
}
// 前面的例子中我们都是像下面这样写代码, 这其实是对原来的 原型对象属性的累加
// 原来的原型对象中有个属性,叫做consturctor
Person.prototype.say = function(){
//todo
}
- 也可以像下面这样
这样设置原型的话,实际上是对原来的原型对象的覆盖,所以说需要像下面这样重新添加constructor的指向。
当然我也试了一下,如果说覆盖原来的原型对象,且不添加contructor的指向,我们使用 instanceof 判断实例对象是否是对应的构造函数类型时,还是能得到正确的结果。
Person.prototype = {
constructor:Person, // 手动修改构造器的指向
height:"20",
weight:"20",
say:function () {
// todo
}
}
2.6 方法之间的相互访问
- 构造函数中的成员方法是可以相互访问的。
function Person(name) {
this.name = name
this.say = function () {
console.log("say")
// 通过这个例子,可以看到,对象的方法中可以直接调用对象的方法
this.eat()
}
this.eat = function () {
console.log("eat")
}
}
- 原型中的方法也是可以相互访问的。
function Person(name) {
this.name = name
}
Person.prototype.say = function(){
console.log("say")
// 原型中的方法也可以相互访问
this.eat()
}
Person.prototype.eat = function(){
console.log("eat")
}
var p1 = new Person("tom")
p1.say()
2.7 覆盖内置对象原型中的方法
像这样就可以实现对原型中的方法进行覆盖的操作。
当然可以通过在原型上添加方法实现对原有封装类的拓展。
// 在现有的js封装类上干这件事,也算是在修改源码
String.prototype.myReverse = function () {
for (var i = 0; i < this.length; i++) {
console.log("发生倒叙")
}
}
var str = "123"
str.myReverse()
JS对象与原型的更多相关文章
- js对象,原型,call,apply浅析
//对象直接量,创建对象最简单的方式是在js里使用对象直接量 var book = { "main title": "js", //属性里有空格,要用引号 &q ...
- JavaScript学习(二)——深入学习js对象的原型与继承
了解对象 什么是对象? …… 这个就不说了 对象的声明的两种方式 var person = new Object(); person.name="linchen"; pers ...
- 深入理解JS对象和原型链
函数在整个js中是最复杂也是最重要的知识 一个函数中存在多面性: 1.它本身就是一个普通的函数,执行的时候形成的私有作用域(闭包),形参赋值,预解释,代码执行,执行完 成后栈内存销毁/不销毁. 2.& ...
- JS对象与原型链
每个函数都存在一个prototype的属性,然后这个属性值为一个对象,我们称之为原型对象 每个对象都存在着一个隐藏的属性"__proto__" 这个属性引用了创建这个对象的函数的p ...
- js对象4-js原型--杂志
提问:在js中什么是原型 prototype 每个学js的人都有自己的解释,网上一大堆的解释与应用,但是看了他们的解释表示还是不理解怎么办?(原因是他们说的太天花乱坠了) 官方手册解释:prototy ...
- JS对象、原型链
忘记在哪里看到过,有人说鉴别一个人是否 js 入门的标准就是看他有没有理解 js 原型,所以第一篇总结就从这里出发. 对象 JavaScript 是一种基于对象的编程语言,但它与一般面向对象的编程语言 ...
- 前端开发JS——对象与原型
27.创建对象 ①工厂模式批量创建对象 缺点:无法对象识别,即所有对象都是Object类型;方法内存空间浪费/封装不太完善 function sayName(){ //可以有效节省内存空间 c ...
- JS对象、原型、this学习总结
1.对象是函数创建的,而函数却又是一种对象.(属性的集合) 2.每个函数都有一个属性叫做prototype.这个prototype的属性值是一个对象,默认的只有一个constructor的属性,指向这 ...
- js 对象细节
原型和原型链 在对象自身身上找不到指定属性时,就会到这个对象的原型__proto__上找,原型也是指向一个对象,在这个对象上还找不到对应属性,则继续到原型上来找...以上过程形成原型链. 访问对象的原 ...
随机推荐
- tf.train.GradientDescentOptimizer 优化器
tf.train.GradientDescentOptimizer(learning_rate, use_locking=False,name='GradientDescent') 参数: learn ...
- Tomorrow - 地形生成(1)
原理很简单,请不要喷. 效果展示 种子输入框 种子为12345的地形 种子为23456的地形 代码展示 globalvar map random_set_seed(real(get_string ...
- 【Java】Operator 运算符/操作符
Operator 运算符/操作符 什么是操作符? 一个表示特定的数学或逻辑操作的符号 算术运算符 加 + 减 - 乘 * 除 / 取模 % 前置自运算 ++ a .--b 后置自运算 a++ .b-- ...
- Jmeter连接mysql数据库?so easy!!!
一.确保mysql数据库能够通过Navicat等远程连接工具连接. 注意:一定是确保能使用navicat连接,而不是dos窗口! 比如笔者需要查询ecshop数据库下的ecs_admin_user表, ...
- 第九节:os、sys、json、pickle、shelve模块
OS模块: os.getcwd()获取当前路径os.chdir()改变目录os.curdir返回当前目录os.pardir()父目录os.makedirs('a/b/c')创建多层目录os.remov ...
- delphi 捕捉全局异常错误的方法
private { Private declarations } public procedure GlobalExceptionHandler(Sender: TObject; E: ...
- 小程序—银行、券商们下一代APP的进阶方向
传统金融机构们的App——尤其以手机银行.手机证券为最,发展到今天,已经产生一系列的问题:从用户角度看,体验普遍不好.高度同质化:从业务运营角度看,几乎没有什么“运营”的抓手:从IT角度看,投入产出比 ...
- stand up meeting 1/19/2016
part 组员 工作 工作耗时/h 明日计划 工作耗时/h UI 冯晓云 准备最后的发布和整个开发的整理总结 6 继续releas ...
- 实体识别中,或序列标注任务中的维特比Viterbi解码
看懂这个算法,首先要了解序列标注任务 QQ522414928 可以在线交流 大体做一个解释,首先需要4个矩阵,当然这些矩阵是取完np.log后的结果, 分别是:初始strat→第一个字符状态的 ...
- Python selenium Chrome正在受到自动软件的控制 disable-infobars无效 的解决方法
问题解决 前两天更新了google浏览器版本,今天运行以前的脚本,发现options一个参数的配置不生效了. 运行了几次都发现该参数没有生效,也检查了自己的代码参数,没有写错,于是就有了这一波“网中寻 ...