JavaScript面向对象之类和实例
本文学习JavaScript的构造函数、类和实例、了解原型链和继承
构造函数
什么是构造函数
- 用new调用一个函数,这个函数就被称为“构造函数”,任何函数都可以是构造函数,只需要用new调用它
- 顾名思义,构造函数用来“构造新对象”,它内部的语句将 为新对象添加若干属性和方法,完成对象的初始化
- 构造函数必须用new关键字调用,否则不能正常工作,正因 如此,开发者约定构造函数命名时首字母要大写
构造函数名称首字母约定大写
function 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, '男');一定要记住:一个函数是不是构造函数,要看它是否用new 调用,而至于名称首字母大写,完全是开发者的习惯约定
如果不用new调用构造函数
function People(name, age, sex) { this.name = name; this.age = age; this.sex = sex;}People('小明', 12, '男');People('小红', 10, '女');People('小刚', 13, '男');这个时候会变成一个全局变量name,这个name的值为小刚
构造函数中的this不是函数本身
构造函数的this指向的是空对象,具体参考函数四步走
尝试为对象添加方法
function 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(); //我是小明,我12岁了 规则1 this指向这个对象xiaomingxiaohong.sayHello(); //我是小红,我10岁了xiaogang.sayHello(); //我是小刚,我13岁了这种方式并不推荐:
- 每次
new People(),都会重新创建一个sayHello函数并挂到实例上,它们是不同的函数对象 - 浪费内存,建了 3 个对象,就有 3 份完全一样的函数
- 无法共享方法,方法本质应该是“行为”,不是“每个对象独有数据”。现在写法是:每个对象都“自带一份行为副本”
更推荐的方式:放到 prototype
function People(name, age, sex) { this.name = name; this.age = age; this.sex = sex;}
People.prototype.sayHello = function () { console.log('我是' + this.name + ',我' + this.age + '岁了');};类和实例
类好比是“蓝图”
- 如同“蓝图”一样,类只描述对象会拥有哪些属性和方法, 但是并不具体指明属性的值
构造函数和“类”
- 可以把构造函数看作是类,把new出来看作是实例
- Java、C++等是“面向对象”(object-oriented)语言
- JavaScript是“基于对象”(object-based)语言
- JavaScript中的构造函数可以类比于OOP语言中的“类”, 写法的确类似,但和真正OO语言还是有本质不同,在后续 课程还将看见JS和其他OO语言完全不同的、特有的原型特性
关于更多class。再后续ES6会有更多讲解。
prototype和原型链查找
什么是prototype
-
任何函数都有prototype属性,prototype是英语“原型” 的意思
-
prototype属性值是个对象,它默认拥有constructor属性 指回函数
函数 -》 函数.prototype函数.prototype -> 函数<body><script>function sum(a, b) {return a + b;}console.log(sum.prototype);console.log(typeof sum.prototype);console.log(sum.prototype.constructor === sum);</script></body> -
普通函数来说的prototype属性没有任何用处,而构造函数 的prototype属性非常有用
-
构造函数的
prototype对象,会成为“由它 new 出来的实例的原型对象” -
实例没有 prototype 属性!!实例有的是 *proto*
-
构造函数的 prototype 是“未来实例共享方法的公共原型对象”
┌─────────────────────┐│ Function 构造器 ││ (Person函数本身) │└─────────┬───────────┘│ prototype↓┌────────────────────────────────┐│ Person.prototype │ ←── 公共方法区│ sayHello() 等共享方法 │└─────────┬──────────────────────┘│ __proto__↓┌────────────────────────────────┐│ Object.prototype │ ←── JS顶层原型│ toString() / valueOf() 等 │└─────────┬──────────────────────┘│ __proto__↓null(原型链终点)────────────────────────────────────────────实例对象层┌─────────────────────┐│ const p = new ││ Person("小明") │└─────────┬───────────┘│ __proto__↓┌────────────────────────────────┐│ Person.prototype │└────────────────────────────────┘
一张图解释:

function People(name, age, sex) { this.name = name; this.age = age; this.sex = sex;}var xiaoming = new People('小名', 12, '男');//证明xiaoming的__proto__是People.prototypeconsole.log(xiaoming.__proto__ === People.prototype);//true原型链的查找
JavaScript规定:实例可以打点访问它的原型的属性和方法,这被称为“原型链查找
当访问一个对象的属性/方法时,如果对象本身没有,就会沿着 __proto__ 一层一层往上找,直到找到或到 null 为止。
function People(name, age, sex) { this.name = name; this.age = age; this.sex = sex;}People.prototype.nationality = '中国';//在构造函数的prototype上添加nationality属性var xiaoming = new People('小明', 12, '男');console.log(xiaoming.nationality);//中国;实例可以打点访问原型的属性和方法原型链会有遮蔽效应
function 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);
var tom = new People('汤姆', 10, '男');tom.nationality = '美国';console.log(tom.nationality);//美国hasOwnProperty
-
hasOwnProperty方法可以检查对象是否真正“自己拥有” 某属性或者方法
xiaoming.hasOwnProperty('name'); // truexiaoming.hasOwnProperty('age'); // truexiaoming.hasOwnProperty('sex'); // truexiaoming.hasOwnProperty('nationality');// false
in
-
in运算符只能检查某个属性或方法是否可以被对象访问,不 能检查是否是自己的属性或方法
'name' in xiaoming // true'age' in xiaoming // true'sex' in xiaoming // true'nationality' in xiaoming // true
提前预习
prototype 是“公共区域”,但只有“挂在同一条原型链上的对象”才能访问这个公共区域
function A() {}A.prototype.a = 10;function B() {}const b = new B();console.log(b.a); // undefined为什么是undefined,原型链是这样的:
b ↓ __proto__B.prototype ↓ __proto__Object.prototype ↓null注意:没有经过 A.prototype所以B 和 A 是两条“原型链分支”,默认互不相通
什么时候“可以访问到 A 的 a”?B 的原型链上包含 A.prototype,也是继承的相关
function A() {}A.prototype.a = 10;function B() {}B.prototype = new A(); // 关键!!!b继承了A的原型链const b = new B();console.log(b.a); // 10在prototype上添加方法
这在构造函数章节有提到过:
function 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(); //我是小明,我12岁了 规则1 this指向这个对象xiaomingxiaohong.sayHello(); //我是小红,我10岁了xiaogang.sayHello(); //我是小刚,我13岁了console.log(xiaoming.sayHello === xiaohong.sayHello)//false把方法直接添加到实例身上的缺点:每个实例和每个实例的 方法函数都是内存中不同的函数,造成了内存的浪费
解决办法:将方法写到prototype上
function People(name, age, sex) { this.name = name; this.age = age; this.sex = sex;}
People.prototype.sayHello = function () { console.log('我是' + this.name + ',我' + this.age + '岁了');};原型链的终点
原型链的终点是 null,null 表示“没有原型”,是整个原型链的终点。
验证一下
console.log(Object.prototype.__proto__); // null先来看下这图:

var arr = [11, 22, 33];console.log(arr.__proto__ === Array.prototype);//trueconsole.log(arr.__proto__.__proto__ === Object.prototype);//true继承
先来看个例子:
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 + '开始睡觉zzzz');};
function Student(name, age, sex, scholl, studionumber) { this.name = name; this.age = age; this.sex = sex; this.scholl = scholl; this.studionumber = studionumber;}Student.prototype.study = function () { console.log(this.name + '在学习');};Student.prototype.exam = function () { console.log(this.name + '在考试');};
//关键语句实现继承,把People的原型链继承到Student上Student.prototype = new People();//constructor 是“告诉你这个对象是由哪个构造函数创建的标签”,使用上面的继承后console.log(s.constructor); // People这是错误的Student.prototype.constructor = Student;// 重写&复写 先继承后重写Student.prototype.sayHello = function () { console.log('敬礼,我是' + this.name + '我今年' + this.age + '岁了');};
var lilei = new Student('李雷', 22, '初中', 10010);//调用People类的方法lilei.sayHello();lilei.sleep();lilei.sayHello();People类和Student类的关系
- People类拥有的属性和方法Student类都有,Student类还扩展了一些属性和方法
- Student“是一种”People,两类之间是“isakind of”关系
- 实现继承的关键在于<子类必须拥有父类的全部属性和方法>子类必须拥有父类的全部属性和方法>,同时子类还应该能定义自己特有的属性和方法
- 使用JavaScript特有的原型链特性来实现继承,是普遍的做法
- ES6有新的继承方法
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!














