前端开发系列014-基础篇之Javascript面向对象(三)

一、原型对象相关方法

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
相关推荐
拾光拾趣录17 分钟前
for..in 和 Object.keys 的区别:从“遍历对象属性的坑”说起
前端·javascript
OpenTiny社区28 分钟前
把 SearchBox 塞进项目,搜索转化率怒涨 400%?
前端·vue.js·github
编程猪猪侠1 小时前
Tailwind CSS 自定义工具类与主题配置指南
前端·css
qhd吴飞1 小时前
mybatis 差异更新法
java·前端·mybatis
YGY Webgis糕手之路1 小时前
OpenLayers 快速入门(九)Extent 介绍
前端·经验分享·笔记·vue·web
患得患失9491 小时前
【前端】【vueDevTools】使用 vueDevTools 插件并修改默认打开编辑器
前端·编辑器
ReturnTrue8681 小时前
Vue路由状态持久化方案,优雅实现记住表单历史搜索记录!
前端·vue.js
UncleKyrie2 小时前
一个浏览器插件帮你查看Figma设计稿代码图片和转码
前端
遂心_2 小时前
深入解析前后端分离中的 /api 设计:从路由到代理的完整指南
前端·javascript·api
你听得到112 小时前
Flutter - 手搓一个日历组件,集成单日选择、日期范围选择、国际化、农历和节气显示
前端·flutter·架构