一、构造函数
1. 用new调用函数的四步走
js
new 函数();
JS规定,使用new操作符调用函数会进行"四步走":
- 函数体内会自动创建出一个空白对象
- 函数的上下文(this)会指向这个对象
- 函数体内的语句会执行
- 函数会自动返回上下文对象,即使函数没有
return
语句
四步走详解
jsfunction fun() { this.a = 3; this.b = 5; } var obj = new fun(); console.log(obj);
第1步:函数体内会自动创建出一个空白对象。
第2步:函数的上下文(this)会指向这个对象。
第3步:执行函数体中的语句
第4步:函数会自动返回上下文对象,即使函数没有return语句。
2. 构造函数
用new调用一个函数,这个函数就被称为
"构造函数"
,任何函数都可以是构造函数,只需要用new调用它。顾名思义,构造函数用来“构造新对象”,它内部的语句将 为新对象添加若干属性和方法,
完成对象的初始化
。构造函数必须用new关键字调用,否则不能正常工作,正因 如此,开发者约定
构造函数命名时首字母要大写
。一个函数是不是构造函数,要看它是否用
new
调用,而至于名称首字母大写,完全是开发者的习惯约定。如果不用
new
调用构造函数jsfunction People(name, age, sex) { this.name = name; this.age = age; this.sex = sex; } People('小明', 12, '男'); People('小红', 10, '女'); People('小刚', 13, '男');
- 结果:都会成为全局的变量,且变量的值会依次覆盖,就是小刚 13 男
使用
new
构建jsfunction People(name, age, sex) { this.name = name; this.age = age; this.sex = sex; } var xiaoming = new People('小明', 12, '男'); var xiaohong = new People('小红', 10, '女'); var xiaogang = new People('小刚', 13, '男');
为对象添加方法
jsfunction People(name, age, sex) { this.name = name; this.age = age; this.sex = sex; this.sayHello = function () { console.log('我是' + this.name + ',我' + this.age + '岁了'); }; } var xiaoming = new People('小明', 12, '男'); var xiaohong = new People('小红', 10, '女'); var xiaogang = new People('小刚', 13, '男'); xiaoming.sayHello(); xiaohong.sayHello(); xiaogang.sayHello();
构造函数中的this不是函数本身
3. 类和实例
- Java、C++等是"面向对象"(object-oriented)语言。
- JavaScript是"基于对象"(object-based)语言。
- JavaScript中的
构造函数可以类比于OO语言中的"类"
, 写法的确类似,但和真正OO语言还是有本质不同
, JS和其他OO语言完全不同的、特有的原型特性。
4. prototype和原型链查找
什么是prototype
- 任何函数都有
prototype属性
,prototype是英语"原型"的意思。 - prototype属性值是个对象,它默认拥有
constructor属性指回函数
。 - 普通函数来说的prototype属性没有任何用处,而
构造函数的prototype属性非常有用。
构造函数的prototype属性是它的实例的原型
- 任何函数都有
构造函数的prototype是实例的原型
html<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script> function People(name, age, sex) { this.name = name; this.age = age; this.sex = sex; } // 实例化 var xiaoming = new People('小明', 12, '男'); // 测试三角关系是否存在 console.log(xiaoming.__proto__ === People.prototype); </script> </body> </html>
原型链查找
每个对象都可以有一个原型_proto_,这个原型还可以有它自己的原型,以此类推,形成一个原型链。 查找特定属性的时候,先去这个对象里去找,如果没有的话就去它的原型对象里面去,如果还是没有的话再去向原型对象的原型对象里去寻找… 这个操作被委托在整个原型链上,这个就是我们说的原型链了。
JavaScript规定
:实例可以打点访问它的原型的属性和方法,这被称为"原型链查找"。jsfunction People(name, age, sex) { this.name = name; this.age = age; this.sex = sex; } People.prototype.nationality = '中国'; var xiaoming = new People('小明', 12, '男'); console.log(xiaoming.nationality);
People.prototype.nationality
在构造函数的prototype
上添加nationality
属性。xiaoming.nationality
实例可以打点访问原型的属性和方法。
原型的遮蔽效应
jsfunction People(name, age, sex) { this.name = name; this.age = age; this.sex = sex; } // 往原型上添加nationality属性 People.prototype.nationality = '中国'; // 实例化 var xiaoming = new People('小明', 12, '男'); var tom = new People('汤姆', 10, '男'); tom.nationality = '美国'; console.log(xiaoming.nationality); // 中国 console.log(xiaoming); console.log(tom.nationality); // 美国 //tom本身有nationality时,就不找原型上的nationality属性了,原型链的遮蔽效应 //跟局部变量全局变量差不多
hasOwnProperty
方法可以检查对象是否真正"自己拥有"某属性或者方法。jsxiaoming.hasOwnProperty('name'); // true xiaoming.hasOwnProperty('age'); // true xiaoming.hasOwnProperty('sex'); // true xiaoming.hasOwnProperty('nationality'); // false
in
运算符只能检查某个属性或方法是否可以被对象访问,不能检查是否是自己的属性或方法。js'name' in xiaoming // true 'age' in xiaoming // true 'sex' in xiaoming // true 'nationality' in xiaoming // true
5. 在prototype
上添加方法
- 把方法直接添加到实例身上的缺点:每个实例和每个实例的方法函数
都是内存中不同的函数,造成了内存的浪费
。 - 解决方法:
将方法写到prototype上
。
6. 原型链的终点
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
function People(name, age) {
this.name = name;
this.age = age;
}
var xiaoming = new People('小明', 12);
console.log(xiaoming.__proto__.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null
console.log(Object.prototype.hasOwnProperty('hasOwnProperty')); // true
console.log(Object.prototype.hasOwnProperty('toString')); // true
</script>
</body>
</html>
关于数组的原型链
html<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script> var arr = [344, 45, 34, 23]; console.log(arr.__proto__ === Array.prototype); // true console.log(arr.__proto__.__proto__ === Object.prototype); // true console.log(Array.prototype.hasOwnProperty('push')); // true console.log(Array.prototype.hasOwnProperty('splice')); // true </script> </body> </html>
6. 继承
JavaScript中如何实现继承?
- 实现继承的关键在于:子类必须拥有父类的
全部属性和方法
,同时子类还应该能定义自己特有的属性和方法。 使用JavaScript特有的原型链特性来实现继承,是普遍的做法
- 实现继承的关键在于:子类必须拥有父类的
通过原型链实现继承
html<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script> // 父类,人类 function People(name, age, sex) { this.name = name; this.age = age; this.sex = sex; } People.prototype.sayHello = function () { console.log('你好,我是' + this.name + '我今年' + this.age + '岁了'); }; People.prototype.sleep = function () { console.log(this.name + '开始睡觉,zzzzz'); }; // 子类,学生类 function Student(name, age, sex, scholl, studentNumber) { this.name = name; this.age = age; this.sex = sex; this.scholl = scholl; this.studentNumber = studentNumber; } // 关键语句,实现继承 Student.prototype = new People(); Student.prototype.study = function () { console.log(this.name + '正在学习'); } Student.prototype.exam = function () { console.log(this.name + '正在考试,加油!'); } // 重写、复写(override)父类的sayHello Student.prototype.sayHello = function () { console.log('敬礼!我是' + this.name + '我今年' + this.age + '岁了'); } // 实例化 var hanmeimei = new Student('韩梅梅', 9, '女', '慕课小学', 100556); hanmeimei.study(); hanmeimei.sayHello(); hanmeimei.sleep(); var laozhang = new People('老张', 66, '男'); laozhang.sayHello(); </script> </body> </html>