JavaScript 对象及对象的创建


前言

  • 即使不了解Javascript的设计原理和继承机制这些东西的时候,我们依然能很好的使用Javascript去设计前端页面及功能。这得力在传统jquery时的前端开发我们把大部分的业务逻辑封装在后台,前端代码的逻辑性大大降低,我们只需要使用一点点j和ava语法类似的知识就能写出所需的javascript代码。这时候我们不需要考虑继承,不需要考虑复杂的封装,甚至连this关键字我们都用的极少。但在接触了react,vue这些前端框架之后我觉得我有必要详细的了解与学习Javascript了,Javascript已经变成一个独立于后端的完整的前端框架的一个基础。

对象及对象的创建


  • Javascript是一种基于对象(object-based)的语言,你遇到的所有东西几乎都是对象。但是,它又不是一种真正的面向对象编程(OOP)语言,因为它的语法中没有"子类"和"父类"的概念,也没有"类"(class)和"实例"(instance)的区分。我们举几个例子,一下的都是对象。

var num=3;
var str=“info”;

  • 这些基础的数据类型也是对象但是不在本章的讨论访问之内。

var fun1=function(){…}
fun1(){…}

  • 这两行都是等价的,都生成了一个名为fun1的(函数)对象。

var data={name:‘Lucy’,age:30,getInfo:function(){…}}

  • 这是我们比较常见的对象方式,用大括号的方式创建对象,采用键值对的方式描述对象属性。

var obj=new Object();
obj.name=“Lucy”;

  • 这个对象的结果其实和第三个对象是一样的都是可以用{}来表述的。

  • 我们可以很清楚的看出这都是对象,完全没有类的概念,我们最后一个例子里的Object是类么?不,不是的。接下来就要说明一下对象的创建方式。


  • 对象的创建方式只有一种,那就是

var obj=new Object();
对象 = new关键字 构造函数;

  • 所以我们很清楚了,Object是一个构造函数(对象)。
  • 这么设计的原因是因为当初javascript的设计者并不打算引入"类"(class)的概念,因为一旦有了"类",Javascript就是一种完整的面向对象编程语言了,这好像有点太正式了,而且增加了初学者的入门难度。不过他在设计创建对象的方式上却借鉴了C++和java的语法,引用了new关键字,但是javascript是没有类的?那么new后面跟着是什么呢?它发现java与C++对象的创建都必须执行构造函数(constructor),于是乎他就做了简化的设计,在Javascript语言中,new命令后面跟的不是类,而是构造函数。
  • 所以以上四种范例其本质上都是javascript通过new 构造函数的方式隐式或显式帮我们创建的。我们先详细的了解一下构造函数的创建方式再细说范例中是如何隐式创建的对象。
构造函数 construct

  • 示例:
var man=function(name,age){
	this.name=name;
	this.age=age;
	this.sex='man';
}
var zs=new man('zhangsan',23);
console.log(zs); //[object Object]   {[functions]: , __proto__: { },age: 23,name:"zhangsan",sex: "man"}
console.log(zs.age);//23
console.log(zs.sex);//"man"
  • 可以看到我们根据man构造函数创建了一个对象zs,对象有name,age和sex 属性。在这里面我们可以清楚的看到构造函数给对象设置属性的方式是this关键字,this关键字动态指向的是创建的对象(关于this的其它问题会另起一章介绍),this关键字后跟着的属性或方法就是该对象的属性或方法。但是如果构造方法有返回值,那生成的对象就是构造方法的返回值了,所以大家在写构造方法时要特别注意,示例如下:
var man=function(name,age){
	this.name=name;
	this.age=age;
	this.sex='man';
	return {info:'error'};
}
var zs=new man('zhangsan',23);
console.log(zs);//[object Object] {[functions]: , __proto__: { },info:"error"}
  • 看到zs的值变成了{info:‘error’},所以写构造函数的时候最好也不要写返回值。但是我发另一很奇怪的情况,示例如下

var man=function(name,age){
	this.name=name;
	this.age=age;
	this.sex='man';
	reuturn "hello javascript";
}
var zs=new man('zhangsan',23);
console.log(zs); //[object Object]   {[functions]: , __proto__: { },age: 23,name:"zhangsan",sex: "man"}
  • 我们看到如果返回的是基本类型,整型,字符串类型等的那构造函数还是生效了,这另我十分费解。不过只要记住在我们写构造函数时不去写返回值也就能规避这种问题。
  • 构造函数创建的方式也不是完全完美的,比如看下面的示例中的sex属性,如果通过man构造函数创建出来的对象sex都固定是“man”,那每个对象都要分配内存来存储sex属性是不是有些浪费呢?或者就是有些属性要给所有对象共用的呢?比如对象的方法,完全不需要随着对象变化,该如何处理?
var zs=new man('zhangsan',23);
var ls=new man('lishi',25);
zs.sex='woman';
console.log(zs.sex);//woman
console.log(ls.sex);//man
  • 为了解决这个问题,javascript为构造函数(函数类型对象)提供了prototype属性。
prototype属性

  • prototype只能用于函数对象,如果你给其它非函数对象增加prototype属性,执行器会报出该对象无prototype属性错误。
  • 我们先来一个示例来看prototype如何使用
function man(name,age){
	this.name=name;
	this.age=age;

}

man.prototype.sex='man';
man.prototype.getInfo=function(){
return "name:"+this.name+",age:"+this.age;
};

var zs=new man('zhangsan',23);
var ls=new man('lishi',25);
zs.age=22;

console.log(zs.sex);//man
console.log(ls.sex);//man
console.log(zs.getInfo());//name:zhangsan,age:22
console.log(ls.getInfo());//name:lishi,age:25

man.prototype.sex='woman';

console.log(zs.sex);//woman
console.log(ls.sex);//woman
  • 我们看到构造函数通过prototype属性增加的属性或方法是给所有对象共享的。我们把那些需要共享的方法和属性放在prototype属性里,那些不需要共享的属性和方法,就放在构造函数里面。
  • 而实现过程是这样的,当实例对象一旦创建,将自动引用prototype对象的属性和方法。也就是说,实例对象的属性和方法,分成两种,一种是本地的,另一种是引用的。
  • 我们再看一个示例

function man(name,age){
	this.name=name;
	this.age=age;

}

man.prototype.sex='man';
man.prototype.getInfo=function(){
return "name:"+this.name+",age:"+this.age;
};

var zs=new man('zhangsan',23);
var ls=new man('lishi',25);
zs.sex='unchecked';

console.log(zs.sex);//unchecked
console.log(ls.sex);//man

man.prototype.sex='woman';

console.log(zs.sex);//woman
console.log(ls.sex);//woman
  • 我们看到只有通过prototype属性修改sex对象才能更新全部对象的sex属性,而单单修改某个对象的sex属性是无法全局修改的,对于这个我不知道是如何实现的,有知道的不吝赐教。
  • 我们再看一个示例
man.prototype={
	sex:'man',
	getInfo:function(){
		return "name:"+this.name+",age:"+this.age;
		}
}
  • 这个示例只是prototype下属性的修改方式改为对整个prototype属性的对象进行设置。这种方式可以么?可以,而且修改成这样对上一个示例的输出完全没有影响。但是个人还是不推荐这种方式,因为prototype属性还有一个特别的方法constructor。
constructor

  • 我们先介绍下constructor属性

man.prototype.constructor

  • constructor属性是指向构造方法的引用,也就是说如下两行代码是等价的。

new man();
new man.prototype.constructor();

  • 看起来好像没什么用,但是我们知道prototype可以把自己的属性传递给对象的。也就是说我们可以通过constructor来比较两个对象是不是同一个构造器的实例,示例如下:

ls.constructor===zs.constructor //true

  • 所以通过man.prototype={…}的方式设置共享属性会破坏constructor属性。虽然好像破坏了在我们的使用中不会产生任何影响,但我们还是要知道有这个属性的存在及prototype的作用。
  • prototype还有一些其他方法,但是通过man.prototype={…}方式是破坏不了,只是因为所有对象的这些方法都是一致的,比如isPrototypeOf,hasOwnProperty方法,提供几个示例,就不详述了。
  • isPrototypeOf 示例是不是来自该构造函数
console.log(man.prototype.isPrototypeOf(zs)); //true
console.log(man.prototype.isPrototypeOf(ls)); //true 
  • hasOwnProperty 每个实例对象都有该方法,用来判断某一个属性到底是本地属性,还是继承自prototype对象的属性
console.log(zs.hasOwnProperty("name")); // true
console.log(zs.hasOwnProperty("sex")); // false 

  • 我们再看一下最开始我们说的那个问题,所有的对象生成本质上都是通过构造函数生成的。查看的方法很简单,我们知道每个对象都有一个constructor方法,我们只要输出每个对象的constructor方法就能找到他的构造函数。
var str="hello javascript";
console.log(str.constructor);//function String() {[native code]}

var fun1=function(name){this.name=name}
console.log(fun1.constructor);//function Function() {[native code]}


var obj={name:"zs"};
console.log(obj.constructor);//function Object() {[native code]}
其它问题

  • 我们说了构造函数,什么样才能算是构造函数呢?所有的函数都可以是构造函数,所有的函数也只有函数可以拥有有prototype属性。而函数也是对象,如上面例子的fun1函数,它也是由Function()构造函数生成的,fun1是函数对象,你可以把fun1当对象用,给他设置属性,方法都是完全没问题的,但是fun1.prototype设置的属性也只能给new fun1()生成的对象使用,不能混淆了。

  • 在阮一峰老师的博客中还说了javascript的继承机制是依靠prototype属性(原型链{prototype chain}模式)来实现的,我个人还是不太理解,因为通过prototype设置“父对象”的属性和方法,只能变成“继承”共享的属性和方法,每个对象自己私有的属性和方法是无法继承的,还是有一些不一样的。而且说是共享只能说是统一赋值,我们通过对象去修改prototype提供的属性并不会影响其他对象的该属性。说是继承但是不太一样。

  • 在理解javascript的对象及对象的创建时要牢记两点,一个是javascript内(几乎)所有的都是对象,并且对象都有构造函数和一些默认方法。比如我有时忘记这两件事,写一些奇怪的范例时就很懵逼,为什么会出现这个结果。如我常忘记prototype属性指向的也是对象,对象也就会有constructor,isPrototypeOf 等这些方法。

参考


  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值