资讯专栏INFORMATION COLUMN

面向对象小记

mmy123456 / 944人阅读

摘要:面向对象原型面向对象想开一个车,你不需要自己去造一个车,只需要一把钥匙,车对于你来说就是一个对象。使用构造函数模式和原型模式组合初学笔记,个人记录备忘,如有谬误,欢迎指正。

面向对象、原型 面向对象

想开一个车,你不需要自己去造一个车,只需要一把钥匙,车对于你来说就是一个"对象"。
在JavaScript中的"对象", 具有属性,当属性的值是一个函数时,那么此属性就是这个对象的方法.访问属性的方式参考Object介绍

如何创建对象?

首先想到的是字面量创建对象,其次是创建一个对象实例,然后一次对对象属性进行赋值.但是无论这两种那个方法都有个缺点,当需要重复创建大量相同的对象时,产生大量代码这是很不理想的,这时想到函数的一个功能就是讲重复代码进行简化,以便多次使用,那么构造函数形式创建多个对象方式就是理想的选择了.

什么是构造函数?
function outPut() {
    console.log("hello")
}

function Person (name,age) {
    this.name2 = name;
    this.age2 = age;
    this.sayName = function () {
        console.log("this.name2")
    };
}

var person2 = new person("Kangkang" , 20)

// 分别调用

这里有几点需要注意,首先是这个this是谁? new操作符有什么作用?

this指向的创建的实例,这里指的是 person2, 而new操作符可以创建一个对象实例.

使用new通过构造函数方式创建对象的实际包含的步骤有:

创建一个新对象,

将构造函数的作用域赋给新对象,此时this已经指向新对象,操作this等同操作新对象

执行构造函数里的代码

返回创建的新对象

如何检测对象的类型? (检测操作符右边的函数原型是否存在于左边的实例的原型链上)

使用instanceof 操作符,返回布尔值.

p1 instanceof Person;
p1 instanceof Object;

第二个检测,无论是自己创建的构造函数对象还是字面量对象,均为true,因为他们均继承自Object. 对于某个构造函数创建的实例当其构造函数的原型被改变后使用此操作符发现返回false ,原因在于在实例的原型链上构造函数的原型是旧的对象,不是新的对象,自然也就不对了。instanceof-MDN

那么对于构造函数创建的对象里的方法,每次创建实例时都要重新创建,如何解决?

思路是将这个方法提到实例的外面去,如果是全局对象不就可以直接调用了吗?可是全局函数对于封装来说可没好处,所以原型模式也就有了用处.

原型

原型是定义能够被对象实例所使用的属性和函数的一种方式.原型的属性会变成实例的属性.

这段话不容易理解.一点点分析,对象的原型属性(prototype)指向的是一个对象,对象里存的属性和方法在对象实例后(即构造函数方法创建的新对象) 可以被实例对象访问使用.

function Person() {
  
}
Person.prototype.name = "Kangkang"
Person.prototype.age = 20
Person.prototype.sayName = function () {
  console.log(this.name)
}

var person1 = new Person()
person1.sayName() // "Kangkang"

var person2 = new Person()
person2.sayName() //"Kangkang"

console.log(person1.sayName == person2.sayName) //true

var person3 = new Person() 
person3.name = "Mary"
person3.sayName() //"Mary"

在函数创建的时候,prototype属性就被创建出来了,通过构造函数生成的实例在浏览器下支持一个属性__proto__ 查看prototype(原型里的对象)

如图所示
显示的是当一个实例在自己属性中找不到相应的方法或者属性时往上一层寻找的情况。

检测某个属性属于实例还是原型:

person1.hasOwnProperty("name") 当这个属性是实例的返回true,是原型的返回false

注意到在实例中重写某个属性会阻止访问原型中的这个属性,可是不会影响到原型中的属性。

多带带操作的in操作符

使用这个操作符可以检测某个属性是否可以通过对象访问,这也就指的是无论这个属性是实例中的还是原型中的,要知道实例可以通过 __proto__ 访问,由上图可知

"name" in Person1 可以访问返回true,否则返回false

这个操作符可以和上面的hasOwnperty() 结合使用判断某个属性是否存在且存在于哪里。

如何为原型添加属性和方法?

由上面介绍可知,通过Person.prototype.name = "kangkang"
这种方法甚是繁琐,多个属性、方法将会导致反复的输入,所以对象字面量的书写方式就能很好解决这个问题。

function Person () {
    
} 

Person.prototype = {
    name : "Kangkang",
    age: 20,
    job: "student",
    sayName = function() {
        console.log(name)
    }
}
防止实例与原型的链断开

谨慎在初始化原型后再将其改变为另外一个对象

function Person () {
    
} 

var friend = new Person()
Person.prototype.sayHi = function() {
    console.log("hi")
}

friend.sayHI();

// 在创建对象后再对原型添加方法依然有效

下面这种情况:

function Person () {
    
} 

var friend = new Person()

Person.prototype = {
    name : "Kangkang",
    age: 20,
    job: "student",
    friends: ["xiaojun","xiaoming"],
    sayName : function() {
        console.log(name)
    }
    
friend.sayName();// error

这种情况相当于在原型初始化后又重新生成对象并且赋值,而原来的实例是指向初始化时的对象地址,故无法访问新的原型里的属性和方法.

原型对象和属性的区别?

如下程序:

function Person () {
    
} 

Person.prototype = {
    name : "Kangkang",
    age: 20,
    job: "student",
    friends: ["xiaojun","xiaoming"],
    sayName : function() {
        console.log(name)
    }
}

var person6 = new Person()
var person7 = new Person()

// person6.name  = "Mary"

// console.log(person6.name)
// console.log(person7.name)

// person6.sayName = function() {
//   console.log("hi")
// }
person6.friends.push("xiaohong")
console.log(person6.friends)
console.log(person7.friends)

console.log(person6.sayName == person7.sayName)

由上面的代码得出:

当对原型中基本类型值属性和方法在实例中重新添加会阻止寻找到原型中的属性和方法,当用delete 操作符删除后依然可以找到原型中的属性和方法,也就是说在实例中重定义这两种情况不会影响原型中的属性和方法,但是如果原型中的属性对应的值是引用类型,由于引用类型的特性,实例得到了相同的这个属性的地址,又由于原型是实例公用的方法,所以当任一实例操作这个引用值属性时都会影响到其他这个对象的实例。所以组合使用构造函数和原型也就很好解决这个问题。

使用构造函数模式和原型模式组合
function Person (name, age, job) {
  this.name = name
  this.age = age
  this.job = job
  this.friends = ["kangkang","mary"]
}
Person.prototype = {
  constructor : Person,
  sayName : function () {
    console.log(this.name)
  }
}

var person1 = new Person("xiaojun",22,"student")
var person2 = new Person("xiaowang", 21, "student")

person1.friends.push("xiaozhang")

console.log(person1.friends) // ["kangkang", "mary", "xiaozhang"]
console.log(person2.friends) // ["kangkang", "mary"]
console.log(person1.friends === person2.friends) //false
console.log(person1.sayName === person2.sayName) //true

初学笔记,个人记录备忘,如有谬误,欢迎指正。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/94358.html

相关文章

  • Java抽象类和接口小记

    摘要:抽象类和接口小记抽象类和接口实现了的多态多态是面向对象程序语言的核心在项目开发过程中其实很少使用抽象类接口用得比较多今天小记一下抽象类和接口的区别抽象类抽象类不能被实例化抽象类可以继承可以定义变量可以定义构造方法抽象方法的要显式的写出来其子 Java抽象类和接口小记 Java抽象类和接口实现了java的多态.多态是面向对象程序语言的核心,在项目开发过程中,其实很少使用抽象类,接口用得比...

    Gemini 评论0 收藏0
  • react-router 升级小记

    摘要:前言最近将公司项目的从版本升到了版本,跟完全不兼容,是一次彻底的重写。升级过程中踩了不少的坑,也有一些值得分享的点。没有就会匹配所有路由最后不得不说升级很困难,坑也很多。 前言 最近将公司项目的 react-router 从 v3 版本升到了 v4 版本,react-router v4 跟 v3 完全不兼容,是一次彻底的重写。这也给升级造成了极大的困难,与其说升级不如说是对 route...

    isLishude 评论0 收藏0
  • 多线程小记

    摘要:死亡状态有两个原因会导致线程死亡方法正常退出而自然死亡。一个未捕获的异常终止了方法而使线程猝死。注意,放入的线程不必担心其结束,超过不活动,其会自动被终止。线程间相互干扰描述了当多个线程访问共享数据时可能出现的错误。 线程 进程与线程的区别 线程是指进程内的一个执行单元,也是进程内的可调度实体。一个程序至少有一个进程,一个进程至少有一个线程。 线程的五大状态 新建状态(New):例如...

    suxier 评论0 收藏0
  • [每日小记]如何创建一个'干净'的map

    摘要:以上的描述说,此方法有两个参数,新创建对象的原型对象。创建一个干净的对象,我们就要借助上面的这个方法了是基本数据类型,是没有原型的,所以讲作为第一个参数传入创建出来的对象就是干净的对象。这个对象不会继承任何。 什么叫干净的map 一般声明一个map对象我们使用字面量的方法 let map = {}; 我们知道,使用字面量声明的对象其实就是默认继承了Object对象,也就是说这个对象拥有...

    happen 评论0 收藏0
  • python小记

    可变数据对象与不可变数据对象 不可变数据对象:不能改变对象本身,只能改变引用的指向。具体包括数字、字符串、元组可变数据对象:可以改变对象自身。具体包括列表、词典

    joyqi 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<