一、原型对象相关方法
java
❏ in 关键字
❏ instanceof
❏ hasOwnProperty方法
❏ constructor构造器属性
❏ isProtoTypeOf方法
in关键字
作用 用来检查对象中是否存在某个属性(不区分实例属性和原型属性)
语法 "属性名" in 对象
xml
<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("属性名")
xml
<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>
如何判断某对象中存在且只存在某个原型属性?
vbnet
function isProperty(obj, property) {
return !obj.hasOwnProperty(property) && (property in obj);
}
constructor构造器属性
-
原型对象中的
constructor
属性指向对应的构造函数 -
实例对象中的
constructor
指向对应的构造函数,其中这里的constructor
就是从原型中获取的constructor是实例对象中的原型属性而非实例属性
javascript
//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(对象)
xml
<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 构造函数
xml
<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中实现继承的几种方式
markdown
> ① 原型式继承
> ② 原型链继承
> ③ 经典继承(借用构造函数)
> ④ 组合继承
其他语言中继承通常通过类来实现,js中没有类的概念,js中的继承是某个对象继承另外一个对象,是基于对象的。
原型式继承
原型式继承的方式A
xml
<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
xml
<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
javascript
//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
上面的方法可以继承超类型中的原型属性和原型方法,但是无法继承实例属性和实例方法
原型链继承
实现思想 利用原型(链)让一个对象继承另一个对象的属性和方法
实现本质 重写原型对象
原型链结构说明
html
① 每个构造函数都有原型对象
② 每个对象都有自己的构造函数
③ 每个构造函数的原型都是一个对象
④ 那么这个构造函数的原型对象也有自己的构造函数
⑤ 那么这个构造函数的原型对象的构造函数也有自己的原型对象
以上形成一个链式的结构,称之为原型链
原型链中的属性搜索原则
less
当访问某个对象的成员的时候,采取的搜索策略是:
① 先在自身中查找,如果找到则直接使用
② 如果在自身中没有找到,则去当前创建当前对象的构造函数的原型对象中查找
(1)如果找到了则直接使用
(2)如果在该原型对象中没有找到,则继续查找原型对象的原型对象
[1] 如果找到则直接使用
[2] 如果在原型对象的原型对象中也没有找到,则继续向上搜索....
`→` 重复上面的过程,直到Object的原型对象,若还是没有,则返回undefined(属性)或报错(方法)。
基本写法·代码示例
javascript
//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()
② 完成继承之后,不能使用字面量的方式来创建原型[因为会切断原型]
③ 注意重写原型对象的位置,必须先实现原型继承,然后再设置子对象的原型属性和原型方法
原型链继承存在的问题
① 在创建子类型的实例时,不能向父类型的构造函数中传递参数
② 父对象的实例属性会转换为子类型的原型属性,如果父类型的实例成员是引用类型则会存在共享问题
scss
//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方法)超类型|父类型构造函数
scss
//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方法
的时候向构造函数传递参数
② 解决实例对象共享问题,通过调用父对象的构造函数来实现每个子类型(对象)的实例对象均拥有一份父类型实例属性和方法的副本。
经典继承存在的问题
① 冒充继承的方法无法实现函数的重用
② 无法继承父对象的原型属性和原型方法
组合继承(伪经典继承)
基本思想
go
> ① `使用原型链实现对原型属性和方法的继承`
> ② `通过伪造(冒充)构造函数来实现对实例属性的继承`
代码示例
scss
//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
说明 上述类型和其他的引用类型类似,同时也具备与各自的基本类型相应的特殊行为,每当我们读取一个基本类型的值的时候,后台就会创建一个对应的基本包装类型对象,从而让我们能够调用一些方法来操作这些数据。
ini
var str = '测试字符串';
console.log(str.length); //5
console.log(str.substring(2)); //字符串
思考 属性和方法本是对象的特征,字符串如何能够拥有length属性以及其他类似subString等方法,内部怎么实现的?
基本类型值并不是对象,因此从逻辑上讨论他们不应该有属性和方法。 内部的具体处理
html
(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);
基本包装类型的代码示例
javascript
//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关键字调用构造函数方式创建的是基本数据类型值。
ini
var str1 = new String('hello');
var str2 = 'hello';
var str3 = String('hello');
说明:以上代码中,str1是对象,而str2和str3是字符串(基本数据类型值)
相等问题
基本类型值判断相等 => 值相等
引用类型值判断相等 => 值相等且引用相等
对象是引用类型,因此在判断相等的时候有诸多的注意点和容易出错的地方。
ini
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