讲给Android程序员看的前端系列教程(40集免费视频教程+源码)
版权声明
- 本文原创作者:谷哥的小弟
- 作者博客地址:http://blog.csdn.net/lfdfhl
参考资料
JavaScript虽然是面向对象的,但是它并没有严格的"父类"和"子类"的概念;也没有"类"和"实例"的区分;当然也没有Java中通过extends来实现继承的机制。所以,JavaScript只能通过别的方式来扩展和继承原有的JavaScript对象。
函数的原型对象
在JavaScript中,当我们创建一个函数浏览器就会自动在内存中创建一个与之对应的对象,而且函数的prototype属性指向了该对象;也就是说:函数的prototype属性的值是正是这个对象 。我们把这个对象称为函数的原型对象,简称函数的原型。该对象默认会有一个属性constructor指向了此函数;也就是说:对象的constructor属性的值正是这个函数。
示例如下:
<!DOCTYPE html>
<!-- 本文作者:谷哥的小弟-->
<!-- 博客地址:https://blog.csdn.net/lfdfhl-->
<html>
<head>
<meta charset="utf-8">
<title>prototype</title>
</head>
<body>
<script type="text/javascript">
function Person(){
}
console.dir(Person);
</script>
</body>
</html>
图示如下:
使用构造函数创建对象
当使用new 构造函数( );
创建对象时,该新建对象就存在一个默认的属性prototype指向构造函数的原型对象。
示例如下:
<!DOCTYPE html>
<!-- 本文作者:谷哥的小弟-->
<!-- 博客地址:https://blog.csdn.net/lfdfhl-->
<html>
<head>
<meta charset="utf-8">
<title>prototype</title>
</head>
<body>
<script type="text/javascript">
function Person(){
}
console.dir(Person);
var p1=new Person();
console.dir(p1);
</script>
</body>
</html>
图示如下:
要点概述:
- 创建p1对象时虽然使用的是Person构造函数,但是创建完毕之后p1对象其实与Person构造函数没有任何关系了,p1对象的__proto__属性指向的是Person构造函数的原型对象。
- 假若使用new Person()创建多个对象,则多个对象都会同时指向Person构造函数的原型对象。
- 假若我们手动为原型对象添加属性和方法,那么Person的对象p1、p2、p3…会共享这些在原型对象中添加的属性和方法。
修改函数的原型对象
当我们修改函数的原型对象后使用new 构造函数( );
创建对象时每个新建对象都可访问原型对象修改后的属性或方法。
示例如下:
<!DOCTYPE html>
<!-- 本文作者:谷哥的小弟-->
<!-- 博客地址:https://blog.csdn.net/lfdfhl-->
<html>
<head>
<meta charset="utf-8">
<title>prototype</title>
</head>
<body>
<script type="text/javascript">
function Person() {
}
Person.prototype.gender = "male";
Person.prototype.age = 20;
Person.prototype.sayHello=function (){
alert("Hello");
};
var p1 = new Person();
var p2 = new Person();
p1.sayHello();
document.write("p1.gender="+p1.gender+" , p1.age="+p1.age+"<br />");
p2.sayHello();
document.write("p2.gender="+p2.gender+" , p2.age="+p2.age+"<br />");
</script>
</body>
</html>
结果如下:
在该示例中,我们为原型对象添加了属性gender和age以及方法sayHello( )。所以,利用Person构造函数创建的p1和p2对象都可以访问gender和age属性以及调用sayHello( )方法。
假若对象中的属性或方法与原型对象中的属性和方法重名了,又会发生什么呢?请看下面的例子。
示例如下:
<!DOCTYPE html>
<!-- 本文作者:谷哥的小弟-->
<!-- 博客地址:https://blog.csdn.net/lfdfhl-->
<html>
<head>
<meta charset="utf-8">
<title>prototype</title>
</head>
<body>
<script type="text/javascript">
function Person() {
}
Person.prototype.gender = "male";
Person.prototype.age = 20;
Person.prototype.sayHello=function (){
alert("Hello");
};
var p1 = new Person();
p1.gender="female";
p1.age=18;
p1.sayHello=function (){
alert("hi");
}
p1.sayHello();
document.write("p1.gender="+p1.gender+" , p1.age="+p1.age+"<br />");
var p2 = new Person();
p2.sayHello();
document.write("p2.gender="+p2.gender+" , p2.age="+p2.age+"<br />");
</script>
</body>
</html>
结果如下:
在该示例中:我们为原型对象添加了属性gender和age以及方法sayHello( );与此同时我们为对象p1对象也添加了属性gender和age以及方法sayHello( )。当我们访问p1的属性时或调用p1的方法时,如果在p1对象中找到则直接返回。如果在p1对象中没有找到再去p1对象的__protot__属性指向的原型对象中查找,若查找到则返回否则继续向上查找。
原型对象相关属性总结
prototype属性
prototype属性存在于构造函数中 (其实任意函数中都有该属性,只不过除构造函数以外的函数的prototype属性我们很少关注而已) 它指向了构造函数的原型对象。
constructor属性
constructor属性存在于原型对象中,它指向与之对应的构造函数。
__proto__属性
当调用构造函数创建一个新的对象之后,该对象中默认会有一个__proto__属性,该属性指向构造方法的原型对象。
原型对象相关方法和操作符总结
hasOwnProperty( ) 方法属性
结合之前的讲解和刚才的示例我们知道:当访问对象的属性的时候,该属性既有可能来自对象本身也有可能来自这个对象的__proto__属性指向的原型对象。那么如何判断该属性的来源呢?在此,我们可使用hasOwnProperty( )方法判断属性是否来自对象本身。
示例如下:
<!DOCTYPE html>
<!-- 本文作者:谷哥的小弟-->
<!-- 博客地址:https://blog.csdn.net/lfdfhl-->
<html>
<head>
<meta charset="utf-8">
<title>prototype</title>
</head>
<body>
<script type="text/javascript">
function Person() {
}
Person.prototype.gender = "male";
Person.prototype.age = 20;
Person.prototype.sayHello=function (){
alert("Hello");
};
var p = new Person();
p.name="lili";
// false
document.write(p.hasOwnProperty("gender")+"<br />");
// false
document.write(p.hasOwnProperty("age")+"<br />");
// true
document.write(p.hasOwnProperty("name")+"<br />");
</script>
</body>
</html>
所以,通过hasOwnProperty( )方法可以判断属性是否是在对象本身添加的;但是,该方法不能判断属性是否存在于原型中。
in操作符
in操作符用于判断属性是否存在于这个对象中。但是在查找该属性时,先在对象本身中查找,假若没有找到再去原型对象中查找。也就是说:只要对象本身或原型对象中存在该属性就返回true。
示例如下:
<!DOCTYPE html>
<!-- 本文作者:谷哥的小弟-->
<!-- 博客地址:https://blog.csdn.net/lfdfhl-->
<html>
<head>
<meta charset="utf-8">
<title>prototype</title>
</head>
<body>
<script type="text/javascript">
function Person() {
}
Person.prototype.gender = "male";
Person.prototype.age = 20;
Person.prototype.sayHello=function (){
alert("Hello");
};
var p = new Person();
p.name="lili";
// true
document.write(("gender" in p) +"<br />");
// true
document.write(("age" in p) +"<br />");
// true
document.write(("name" in p) +"<br />");
</script>
</body>
</html>
继承
继承是所有的面向对象的语言最重要的特征之一。
JavaScript中实现继承的方式有多种。其中,最常用和最本质的方式就是通过修改原型对象达到继承的效果。例如:原本有一个构造函数,构造函数中有属性和方法。之后,我们可通过修改原型对象的方式增加属性和方法从而达到继承的目的。
示例如下:
<!DOCTYPE html>
<!-- 本文作者:谷哥的小弟-->
<!-- 博客地址:https://blog.csdn.net/lfdfhl-->
<html>
<head>
<meta charset="utf-8">
<title>prototype</title>
</head>
<body>
<script type="text/javascript">
//构造函数
function Person(name,age) {
this.name=name;
this.age=age;
this.sayHi=function (){
console.log("Hi , My name is "+this.name+" , I am "+age);
}
}
//创建对象
var p1=new Person("xixi",19);
p1.sayHi();
//利用console.dir()打印对象p1
console.dir(p1);
//修改原型对象增加属性
Person.prototype.country="China";
//修改原型对象增加方法
Person.prototype.sayHello=function(){
console.log("Hello , My name is "+this.name+" , I come from "+this.country+" , I am "+this.age);
}
//创建对象
var p2=new Person("papa",21);
p2.sayHi();
p2.sayHello();
//利用console.dir()打印对象p2
console.dir(p2);
</script>
</body>
</html>
在该示例中,我们通过修改原型对象的方式增加属性和方法达到继承的目的!
结果如下:
在该示例中,我特意使用console.dir( )打印了p1对象和p2对象。请仔细观察上图,您会发现:p1和p2的__proto__是相同的,或者说p1和p2的原型对象是相同的!这也再次验证了我们之前的理论:
到了这,您会发现:JavaScript的继承是发生在对象与对象之间的。这与大脑中存留的Java类与类之间的继承大相径庭,截然不同。
鸭式辨型
在讨论函数和对象的时候我们多次提到:JavaScript是弱类型的语言。JavaScript这种与生俱来的特性在给开发人员带来极大便利的同时也不可避免地产生了些许副作用。例如,有时不能确保传递给方法的参数是所期待的而且是合法的。
所以,为了程序的严谨和避免不必要的麻烦,我们可对传入的参数进行辨识,
示例如下:
<!DOCTYPE html>
<!-- 本文作者:谷哥的小弟-->
<!-- 博客地址:https://blog.csdn.net/lfdfhl-->
<html lang="en">
<head>
<meta charset="UTF-8">
<title>鸭式辨型</title>
</head>
<body>
<script type="text/javascript">
function Student(name, age) {
this.name = name;
this.age = age;
}
//printStudentInfo()方法用于打印学生信息
//该方法共有两个输入参数,要求如下:
//第一个输入参数为数字型
//第二个输入参数为Student型
function printStudentInfo(id, student) {
if (typeof(id) == "number" && student instanceof Student) {
console.log("输入的参数合法");
} else {
console.log("输入的参数非法");
}
}
var stu = new Student("lilei", 18);
var id = 9527;
printStudentInfo(id,stu);
</script>
</body>
</html>
结果如下:
在该示例中,在printStudentInfo( )方法的起始处利用typeof和instanceof判断输入参数是否是所需要的类型。这种做法也叫做鸭式辨型——像鸭子一样走路、游泳和嘎嘎叫的家禽就是鸭子!
转载:https://blog.csdn.net/lfdfhl/article/details/100725525