【JS-6】——JavaScript构造函数与原型

上次笔记大致说了一下javaScript中函数的使用,我们这次来看一下构造函数与原型。

使用工厂方法创建对象

我们现在要创建几个对象:

var obj ={
  name:"孙悟空",
  age:18,
  gender:male,
  sayName:function(){
	  alert(this.name);
  }
}

var obj2 ={
  name:"猪八戒",
  age:28,
  gender:male,
  sayName:function(){
  	alert(this.name);
  }
}

var obj3 ={
  name:"沙和尚",
  age:38,
  gender:male,
  sayName:function(){
  	alert(this.name);
  }
}

我们看到了大量的重复代码,程序员有一个特点就是懒,麻烦的事就不去做,所以我们看到这么多大量重复的代码,我们如果要创建多几个对象就会不难反了。

所以,我们以后遇到大量重复的代码,就要把它们提出来。我们现在说使用工厂方法创建对象

function createPerson(name,age,gender){
    //创建一个新的对象
    var obj = new Object();
    //向对象中添加属性
    obj.name = name;
    obj.age = age;
    obj.gender = gender;
    obj.sayName = function(){
 	   alert(this.name);
    }
  	//将创建新的对象返回
		return obj;
}

var obj2 = createPerson("猪八戒",18,"男");
var obj3 = createPerson("白骨精",22,"女");
var obj4 = createPerson("唐僧",66,"男");

console.log(obj2);
console.log(obj3);
console.log(obj4);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-38kNtCac-1588302643980)(/Users/mac/Desktop/MarkDown /Javascript/图片/【JS-6】/1.jpg)]

使用工厂方法创造的对象,使用的构造函数都是Object,所以常见的对象都是Object这个类型,就导致我们无法区分对象类型。

构造函数

  • 构造函数就是一个普通函数,不同的是:构造函数习惯上首字母大写

  • 构造函数和普通函数的区别,就是调用方式的不同。

    • 普通函数是直接调用,而构造函数要使用new关键字来调用

创建一个构造函数,专门用来创建Person对象:

fuction Person(){

}

普通方式调用:

var per = Person();

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-flAPROOT-1588302643982)(/Users/mac/Desktop/MarkDown /Javascript/图片/【JS-6】/2.jpg)]

构造函数调用:

var per = new Person();

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eGSyERL2-1588302643988)(/Users/mac/Desktop/MarkDown /Javascript/图片/【JS-6】/3.jpg)]

使用同一个构造函数构造创建的对象,称为一类对象,也将构造函数称为一个类。我们将通过该构造函数创建的对象,称为该类的实例。

function Person(name, age, gender){
  this.name = name;
  this.age = age;
  this.gender = gender;
  this.sayName = function(){
    alert(this.name);
    }
}

//这三个对象都是Person类的实例
var per = new Person("阿牛",18,"男");
var per2 = new Person("女娃",18,"女");
var per3 = new Person("奔波霸",22,"男");

使用instenceof可以检查一个对象是否是一个类的实例。如果是,则返回true,否则返回false。

语法对象 instenceof 构造函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Brklnia8-1588302643991)(/Users/mac/Desktop/MarkDown /Javascript/图片/【JS-6】/4.jpg)]

所有的对象都是Object的后代。

构造函数的执行流程

  1. 立刻创建一个新的对象。
  2. 将新建的对象,设置为函数中的this,在构造函数中可以使用this来引用新建的对象。
  3. 逐行执行函数中的代码 。
  4. 将新建的对象作为返回值返回。

现在我们再来重申一下之前说过的this

  1. 当以函数的形式调用的时候,this就是window。
  2. 当以方法的形式调用的时候,谁调用方法this就是谁。
  3. 当以构造函数的形式调用的时候,this就是创建的那个对象。

使用toString()

当我们写这样一段代码时:

function Person(name, age, gender){
  this.name = name;
  this.age = age;
  this.gender = gender;
}

var per = new Person("孙悟空",18,"男");
document.write(per);			

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-psyJ3wC6-1588302643993)(/Users/mac/Desktop/MarkDown /Javascript/图片/【JS-6】/10.jpg)]

发现在页面中总是返回这个奇怪的东西。

因为,当我们直接在页面中打印一个对象时,实际上是输出的对象的toString()方法的返回值。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B1eVS2wZ-1588302643995)(/Users/mac/Desktop/MarkDown /Javascript/图片/【JS-6】/11.jpg)]

那toString()方法哪来的那??

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tB3mMgiE-1588302643996)(/Users/mac/Desktop/MarkDown /Javascript/图片/【JS-6】/12.jpg)]

原来是在Object的原型里。

如果我们希望在输出对象时,不输出[object object],可以为对象添加一个toString()方法。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aP7GanHn-1588302644001)(/Users/mac/Desktop/MarkDown /Javascript/图片/【JS-6】/13.jpg)]

我们也可以这样修改:

Person.prototype.toString = function(){
	return "Person[ name = " +this.name + " , age = " + this.age + " , gender = " + this.gender+" ]";
}
document.write(per);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4QNBvoPh-1588302644001)(/Users/mac/Desktop/MarkDown /Javascript/图片/【JS-6】/14.jpg)]

构造函数的修改

还是这个代码,我们来看看有没有什么问题:

function Person(name, age, gender){
  this.name = name;
  this.age = age;
  this.gender = gender;
  this.sayName = function(){
  	alert("hello 大家好,我是"+this.name);
  };
}

var per = new Person("孙悟空",18,"男");
var per2 = new Person("猪八戒",28,"男");

per.sayName();
per2.sayName();
  • 在Person构造对象中,为每一个对象都添加了一个sayName方法,

  • 目前我们的方法是在构造函数内部创建的,也就是构造函数没执行一次就会常见一个新的sayName方法,也就是说,所有实例的sayName都是唯一的。

  • 这样就导致构造函数执行一次,就会创建一个新的方法,执行10000次就会创造10000个新的方法,而这10000个方法都是一模一样的,这是完全没有必要的。完全可以使所有的对象共享同一个方法。

那怎么办呢??我们可以将sayName提取出来,这样,我们sayName方法只有一个,对构造函数的性能提升了不少。

function Person(name, age, gender){
  this.name = name;
  this.age = age;
  this.gender = gender;
  this.sayName = fun;
}

function fun(){
	alert("hello 大家好,我是"+this.name);
}

var per = new Person("孙悟空",18,"男");
var per2 = new Person("猪八戒",28,"男");

per.sayName();
per2.sayName();

但是这是,我们将函数定义在全局作用域中了,将函数定义在全局作用域中,污染了全局作用域中的命名空间。而且,定义在全局作用域中,也很不安全!!所以尽量不要在全局作用域中定义,那现在又怎么办呢???我们要说原型(prototype)。

原型

  • 我们所创建的每一个函数,解析器都会向函数中添加一个属性prototype。这个属性对应着一个对象,这个对象就是我们所谓的原型对象。

  • 如果函数作为普通函数调用prototype没有任何作用。

  • 当函数以构造函数的形式调用时,它所创建的对象中,都会有一个隐含的属性,指向该构造函数的原型对象。我们可以通过__proto__来访问该属性。

  • 原型对象就相当于一个公共的区域,所有同一类的实例,都可以访问到这个原型对象。我们可以将对象中共有的内容统一设置到原型对象中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6Il2L1pG-1588302644002)(/Users/mac/Desktop/MarkDown /Javascript/图片/【JS-6】/5.jpg)]

  • 当我们访问一个属性或者方法时,先会在自身对象中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uMpWAWpV-1588302644003)(/Users/mac/Desktop/MarkDown /Javascript/图片/【JS-6】/6.jpg)]

我们也可以向MyClass的原型中添加方法:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title></title>
		<script type="text/javascript">
			
			
			function MyClass(){
				
			}
			
			MyClass.prototype.sayHello = function(){
				alert("hello");
			}
			
			
			var mc = new MyClass();
			var mc2 = new MyClass();
			
			mc.sayHello();
			
			
		</script>
	</head>
	<body>
	</body>
</html>

这样既可以调用到我们需要的方法,又不会污染全局作用域中的命名空间。所以可以完善一下:

function Person(name, age, gender){
  this.name = name;
  this.age = age;
  this.gender = gender;
}

Person.prototype.sayName = function(){
	alert("Hello 大家好, 我是"+ this.name);
}

var per = new Person("孙悟空",18,"男");
var per2 = new Person("猪八戒",28,"男");

per.sayName();
per2.sayName();

以后我们创建构造函数时,可以将这些对象共有的属性或者方法,同意添加到构造函数的原型对象中,这样不用想每一个对象添加,也不会影响全局作用域,就可以使每个对象都具有这些属性和方法了。

检测原型

我们之前说过,可以使用in来检查对象中是否包含某属性。

function MyClass(){
	
}

MyClass.prototype.name = "我是原型中的名字";

var mc = new MyClass();

document.write("name" in mc);//true

但是,此时我们并没有向mc对象中添加name属性,而是向其原型添加了name属性,而使用in检查对象中是否含有某个属性时,如果对象中没有,但是原型中有,也会返回true。

  • 可以使用对象的hasOwnProperty()来检测对象自身中是否含有该属性。
  • 使用该方法只有自身中含有该属性时,才会返回true。
document.write(mc.hasOwnProperty("name"));//false

哪么如果你用hasOwnProperty()去检测mc和mc的原型时,有趣的事情就发生了。

document.write(mc.hasOwnProperty("hasOwnProperty"));
document.write("<br />");
document.write(mc.__proto__.hasOwnProperty("hasOwnProperty"));
document.write("<br />");

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MvRUh6lY-1588302644004)(/Users/mac/Desktop/MarkDown /Javascript/图片/【JS-6】/7.jpg)]

我们发现,hasOwnProperty()方法,既不在mc中也不再mc的原型对象中,那它在那里呢??

我们说原型对象还有原型对象!!

document.write(mc.hasOwnProperty("hasOwnProperty"));
document.write("<br />");
document.write(mc.__proto__.hasOwnProperty("hasOwnProperty"));
document.write("<br />");
document.write(mc.__proto__.__proto__.hasOwnProperty("hasOwnProperty"));
document.write("<br />");

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-odj0oGta-1588302644005)(/Users/mac/Desktop/MarkDown /Javascript/图片/【JS-6】/8.jpg)]

原型对象也是对象,所以它也有原型。

  • 所以:当我们使用一个对象的属性或者方法的时候,会先在自身中寻找,如果有,则会直接使用。如果没有,则会去原型对象中寻找。如果原型对象中有,则使用,如果没有则去原型对象的原型对象中寻找,直到找到Object对象的原型。如果依然没有则返回undefined。

    但是,也不是没完没了的。Object对象的原型没有原型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gJyy7ZZK-1588302644006)(/Users/mac/Desktop/MarkDown /Javascript/图片/【JS-6】/9.jpg)]

之前的

【JS-4】——JavaScript关于对象的使用
【JS-5】——javascript关于函数的使用

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值