原型对象相关方法

in关键字
instanceof
hasOwnProperty方法
constructor构造器属性
isProtoTypeOf方法

in关键字

作用 用来检查对象中是否存在某个属性(不区分实例属性和原型属性)

语法 “属性名” in 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script>

//01 提供一个构造函数
function Person(name) {
this.name = name;
}

//02 设置构造函数的原型对象的属性
Person.prototype.sayHello = function () {
console.log("hello");
}

//03 创建对象
var p1 = new Person();

//04 使用in关键字判断对象中是否存在以下属性:name age sayHello
console.log("age" in p1); //false
console.log("name" in p1); //true
console.log("sayHello" in p1); //true

</script>

hasOwnProperty

作用 用来检查对象中是否存在指定的属性(只检查实例属性)

语法 对象.hasOwnProperty(“属性名”)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script>

//01 提供一个构造函数
function Person(name) {
this.name = name;
}

//02 设置构造函数的原型对象的属性
Person.prototype.sayHello = function () {
console.log("hello");
}

Person.prototype.des = "默认的描述信息";

//03 创建对象
var p1 = new Person();

//04 使用hasOwnProperty方法判断该属性是否是对象的实例属性
console.log(p1.hasOwnProperty("age")); //false
console.log(p1.hasOwnProperty("name")); //true
console.log(p1.hasOwnProperty("sayHello")); //false
console.log(p1.hasOwnProperty("des")); //false

</script>

需求 如何判断某对象中存在且只存在某个原型属性?

1
2
3
function isProperty(obj, property) {
return !obj.hasOwnProperty(property) && (property in obj);
}

constructor构造器属性

  • 原型对象中的constructor属性指向对应的构造函数
  • 实例对象中的constructor指向对应的构造函数,其中这里的constructor就是从原型中获取的constructor是实例对象中的原型属性而非实例属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//01 提供一个构造函数
function Person(name) {
this.name = name;
}

//02 设置构造函数的原型对象的属性
Person.prototype.sayHello = function () {
console.log("hello");
};
Person.prototype.des = "默认的描述信息";

//03 创建对象
var p1 = new Person();
function isProperty(obj, property) {
return !obj.hasOwnProperty(property) && (property in obj);
}
console.log(isProperty(p1, "constructor")); //true

isProtoTypeOf

作用 判断是否是某个实例对象的原型对象
语法 构造函数.protoType.isPrototypeOf(对象)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>

function Person() {
}
function Dog() {
}

Person.prototype.name = "嘿嘿";
var p1 = new Person();

console.log(Person.prototype.isPrototypeOf(p1)); //rue
console.log(Object.prototype.isPrototypeOf(p1)); //true
console.log(Dog.prototype.isPrototypeOf(p1)); //false

</script>

instanceof

作用 用于检查对象是否是某个构造函数(类型)的实例
语法对象 instance 构造函数

1
2
3
4
5
6
7
8
9
10
11
<script>

var arr = [1,2,3];
console.log(arr instanceof Array); //true
console.log(Array instanceof Object); //true
console.log(arr instanceof Object); //true

//instanceOf在判断的时候,算上整条原型链
//arr 是Array 和Object 任何一个类的示例

</script>

注意 所有的对象都是Object构造函数(类型)的实例

javaScript面向对象编程(继承)

继承·概念

继承 即通过一定的方式实现让某个类型A获取另外一个类型B的属性或方法。其中类型A称之为子类型,类型B称之为父类型或超类型。

javaScript中的继承

Object是所有对象的父级 | 父类型 | 超类型 js中所有的对象都直接或间接的继承自Object。

继承主要有两种方式:接口继承和实现继承。在js中只支持实现继承,实现继承主要依赖原型链来完成

JavaScript中实现继承的几种方式

① 原型式继承
② 原型链继承
③ 经典继承(借用构造函数)
④ 组合继承

说明其他语言中继承通常通过类来实现,js中没有类的概念,js中的继承是某个对象继承另外一个对象,是基于对象的。

原型式继承

原型式继承的方式A

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script>

//01 提供一个构造函数
function Person(name,age) {
this.name = name;
this.age = age;
}

//02 设置原型对象的属性
Person.prototype.className = "逍遥派1班";

//03 使用构造函数来创建原型对象
var p1 = new Person("张三",10);
var p2 = new Person("李四",20);

//04 打印p1和p2对象中的className属性
console.log(p1.className);
console.log(p2.className);

//结论:对象p1和p2继承了构造函数原型对象中的属性className
//但是这并不是严格意义上的继承

</script>

原型式继承的方式B

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<script>
//01 提供一个构造函数
function Person(name,age) {
this.name = name;
this.age = age;
}

//02 设置原型对象的属性
Person.prototype = {
constructor:Person,
className:"逍遥派1班"
};

//03 使用构造函数来创建原型对象
var p1 = new Person("张三",10);
var p2 = new Person("李四",20);

//04 打印p1和p2对象中的className属性
console.log(p1.className);
console.log(p2.className);

//结论:对象p1和p2继承了构造函数原型对象中的属性className
//注意:使用原型替换的方式实现继承的时候,原有原型对象中的属性和方法会丢失

</script>

原型式继承的方式C

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//01 提供超类型|父类型构造函数
function SuperClass() {
this.name = 'SuperClass的名称';
this.showName = function () {
console.log(this.name);
}
}

//02 设置父类型的原型属性和原型方法
SuperClass.prototype.info = 'SuperClass的信息';
SuperClass.prototype.showInfo = function () {
console.log(this.info);
};

//03 提供子类型
function SubClass() {}

//04 设置继承(原型对象继承)
SubClass.prototype = SuperClass.prototype;
SubClass.prototype.constructor = SubClass;

var sub = new SubClass();
console.log(sub.name); //undefined
console.log(sub.info); //SuperClass的信息
sub.showInfo(); //SuperClass的信息
sub.showName(); //sub.showName is not a function

注意 上面的方法可以继承超类型中的原型属性和原型方法,但是无法继承实例属性和实例方法

原型链继承

实现思想 利用原型(链)让一个对象继承另一个对象的属性和方法
实现本质 重写原型对象

原型链结构说明

1
2
3
4
5
6
① 每个构造函数都有原型对象
② 每个对象都有自己的构造函数
③ 每个构造函数的原型都是一个对象
④ 那么这个构造函数的原型对象也有自己的构造函数
⑤ 那么这个构造函数的原型对象的构造函数也有自己的原型对象
以上形成一个链式的结构,称之为原型链

原型链中的属性搜索原则

1
2
3
4
5
6
7
8
当访问某个对象的成员的时候,采取的搜索策略是:
① 先在自身中查找,如果找到则直接使用
② 如果在自身中没有找到,则去当前创建当前对象的构造函数的原型对象中查找
1)如果找到了则直接使用
2)如果在该原型对象中没有找到,则继续查找原型对象的原型对象
[1] 如果找到则直接使用
[2] 如果在原型对象的原型对象中也没有找到,则继续向上搜索....
`→` 重复上面的过程,直到Object的原型对象,若还是没有,则返回undefined(属性)或报错(方法)。

基本写法·代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//01 提供超类型|父类型
function SuperClass() {
this.name = 'SuperClass的名称';
this.showName = function () {
console.log(this.name);
}
}

//02 设置父类型的原型属性和原型方法
SuperClass.prototype.info = 'SuperClass的信息';
SuperClass.prototype.showInfo = function () {
console.log(this.info);
};

//03 提供子类型
function SubClass() {
}

//04 设置继承(原型对象继承)
SubClass.prototype = new SuperClass();
SubClass.prototype.constructor = SubClass;

var sub = new SubClass();
console.log(sub.name); //SuperClass的名称
console.log(sub.info); //SuperClass的信息
sub.showInfo(); //SuperClass的信息
sub.showName(); //SuperClass的名称

点评 可以继承父类型中的原型属性|原型方法,以及实例属性和实例方法

原型链继承注意点

① 确定原型和实例的关系 instanceof + isPrototypeOf()
② 完成继承之后,不能使用字面量的方式来创建原型[因为会切断原型]
③ 注意重写原型对象的位置,必须先实现原型继承,然后再设置子对象的原型属性和原型方法

原型链继承存在的问题

① 在创建子类型的实例时,不能向父类型的构造函数中传递参数
② 父对象的实例属性会转换为子类型的原型属性,如果父类型的实例成员是引用类型则会存在共享问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//01 提供父对象的构造函数
function SuperType() {
//02 在构造函数中中设置实例属性,该属性为引用类型
this.family = ['哥哥','姐姐','爸爸','妈妈'];
};

//03 提供子对象的构造函数
function SubType() {};

//04 设置原型继承
SubType.prototype = new SuperType();

//05 创建父对象构造函数的实例对象,并对内部的实例化属性进行修改
var subDemo1 = new SubType();
var subDemo2 = new SubType();

alert(subDemo1.family); //哥哥,姐姐,爸爸,妈妈
alert(subDemo2.family); //哥哥,姐姐,爸爸,妈妈

subDemo1.family.push('爷爷','奶奶');
alert(subDemo1.family); //哥哥,姐姐,爸爸,妈妈,爷爷,奶奶
alert(subDemo2.family); //哥哥,姐姐,爸爸,妈妈,爷爷,奶奶

经典继承(借用构造函数)

基本思想 在子类型构造函数的内部调用(需要借助call|apply方法)超类型|父类型构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//01 提供父类型(对象)的构造函数
function SuperType(name) {
//02 在构造函数中中设置实例属性,该属性为引用类型
this.family = ['哥哥','姐姐','爸爸','妈妈'];
//实例属性
this.name = name;
};

SuperType.prototype.info = '父类型的原型属性';
//03 提供子类型(对象)的构造函数
function SubType() {
//经典继承|借用构造函数|伪造对象继承
//SuperType.call(this);

//构造参数传递参数
SuperType.call(this,'张老汉');
};

//04 创建父类型的实例对象,并对内部的实例化属性进行修改
var subDemo1 = new SubType();
var subDemo2 = new SubType();

alert(subDemo1.info); //undefined
alert(subDemo1.family); //哥哥,姐姐,爸爸,妈妈
alert(subDemo2.family); //哥哥,姐姐,爸爸,妈妈

subDemo1.family.push('爷爷','奶奶');
alert(subDemo1.family); //哥哥,姐姐,爸爸,妈妈,爷爷,奶奶
alert(subDemo2.family); //哥哥,姐姐,爸爸,妈妈

//测试构造函数传递参数
alert(subDemo1.name);


注意 经典继承又称为借用构造函数 | 伪造继承,这种方式能够继承父类型的实例属性,但是无法继承父类型的原型属性和原型方法

经典继承的优点

① 可以在调用call方法的时候向构造函数传递参数
② 解决实例对象共享问题,通过调用父对象的构造函数来实现每个子类型(对象)的实例对象均拥有一份父类型实例属性和方法的副本。

经典继承存在的问题

① 冒充继承的方法无法实现函数的重用
② 无法继承父对象的原型属性和原型方法

组合继承(伪经典继承)

基本思想

使用原型链实现对原型属性和方法的继承
通过伪造(冒充)构造函数来实现对实例属性的继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//01 提供父类型的构造函数
function SuperType(name) {
//在构造函数中中设置实例属性,该属性为引用类型
this.family = ['哥哥','姐姐','爸爸','妈妈'];

//实例属性
this.name = name;
};

//原型方法
SuperType.prototype.showName = function () {
console.log(this.name);
}

//02 提供子类型的构造函数
function SubType(name) {
//冒充|伪造 构造参数传递参数
SuperType.call(this,name);
};

SubType.prototype = SuperType.prototype;
//SubType.prototype = new SuperType();

//02 创建父类型的实例对象,并对内部的实例化属性进行修改
var subDemo1 = new SubType('张三');
var subDemo2 = new SubType('张四');

alert(subDemo1.family); //哥哥,姐姐,爸爸,妈妈
alert(subDemo2.family); //哥哥,姐姐,爸爸,妈妈

subDemo1.family.push('爷爷','奶奶');
alert(subDemo1.family); //哥哥,姐姐,爸爸,妈妈,爷爷,奶奶
alert(subDemo2.family); //哥哥,姐姐,爸爸,妈妈

//测试构造函数传递参数
subDemo1.showName(); //张三
subDemo2.showName(); //张四

基本包装类型

基本类型 字符串 + 数值 + null + undefined + 布尔值

为了便于操作基本类型,ECMAScript提供了三个特殊的引用类型:Boolean + Number + String

说明 上述类型和其他的引用类型类似,同时也具备与各自的基本类型相应的特殊行为,每当我们读取一个基本类型的值的时候,后台就会创建一个对应的基本包装类型对象,从而让我们能够调用一些方法来操作这些数据。

1
2
3
var str = '测试字符串';
console.log(str.length); //5
console.log(str.substring(2)); //字符串

思考 属性和方法本是对象的特征,字符串如何能够拥有length属性以及其他类似subString等方法,内部怎么实现的?

基本类型值并不是对象,因此从逻辑上讨论他们不应该有属性和方法。 内部的具体处理

1
2
3
(1)创建String类型的一个实例对象
(2)在实例对象上面读取指定的属性(length),调用指定的方法(subString)
(3)销毁该对象

Number

Number是与数字值相对应的引用类型

创建Number类型的对象:var num = new Number(10);

String

String是字符串的对象包装类型

创建字符串类型的对象:var str = new String('hello World');

Boolean

Boolean是与布尔值对象的引用类型

可以通过调用Boolean构造函数传递参数来创建boolean类型的对象。var bool = new Boolean(true);

基本包装类型的代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//001 String
var str = '测试字符串';
console.log(str.length); //5
console.log(str.substring(2)); //字符串

//002 Number
var num = new Number(10);
console.log(num); //Number {[[PrimitiveValue]]: 10}
console.log(typeof num); //object
console.log(typeof 10); //number

//003 Boolean
var bool = new Boolean(true);
console.log(bool); //Boolean {[[PrimitiveValue]]: true}
console.log(typeof bool); //object
console.log(typeof true); //boolean

基本包装类型的注意点

对象还是基本数据类型值?

对象:通过new 调用构造函数创建出来的是对象
基本数据类型值:直接通过字面量方式赋值|通过省略new关键字调用构造函数方式创建的是基本数据类型值。

1
2
3
4
var str1 = new String('hello');
var str2 = 'hello';
var str3 = String('hello');
说明:以上代码中,str1是对象,而str2和str3是字符串(基本数据类型值)

相等问题

基本类型值判断相等 => 值相等
引用类型值判断相等 => 值相等且引用相等
对象是引用类型,因此在判断相等的时候有诸多的注意点和容易出错的地方。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var str1 = '这是一个字符串';           //基本数据类型
var str2 = String('这是一个字符串'); //基本数据类型
console.log(str1 == str2); //true 相等


var str3 = new String('这是一个字符串'); //引用类型-对象
console.log(str1 == str3); //true //值相等
console.log(str2 == str3); //true //值相等

console.log(str1 === str3); //false //值相等,但是引用不相等
console.log(str2 === str3); //false //值相等,但是引用不相等

//判断下面的变量是否相等
var num1 = 10; //基本数据类型
var num2 = new Number(10); //对象
console.log(num1 == num2); //true
console.log(num1 === num2); //false

var bool1 = true;
var bool2 = new Boolean(true);
console.log(bool1 == bool2); //true
console.log(bool1 === bool2); //false