首页 > 技术文章 > 面向对象编程(2)-类与原型

Shyno 2021-03-17 19:27 原文

1.用函数代替对象储存变量

变量

const obj1 = {
  a: 1,
  b: ['1', '2'],
  c: function () {
    return 'name'
  }
}

 函数:实际上最终还是放在对象里

const getObj=()=>{
  return {
    a: 1,
    b: ['1', '2'],
    c: function () {
      return 'name'
    }
}
}

    首先,变量保存,在你申明的时候就已经把这个变量的内容存在内存里的.用变量,存的就是变量,用函数,存的就是函数.换而言之,对象中存了a,b,c.而函数中并没有,函数返回的那个对象只有在函数执行的时候才被创建.

也就是说,变量多次拿的都是同一个.而函数,每次返回的都是一个新的对象,它们之间互不相同,互不干扰.

变量(因为是同一个,所以改一个,大家都改了)

const obj1 = {
  a: 1,
  b: ['1', '2'],
  c: function () {
    return 'name'
  }
}
let objj = obj1
objj.a = 2
console.log('obj', obj1, objj)//2,2

 函数(原函数保存的数据不会因为后续的操作改变,保持独立,更加可控)

const getObj = function () {
  return {
    a: 1,
    b: ['1', '2'],
    c: function () {
      return 'name'
    }
  }
}
let objj = getObj()
objj.a = 2
console.log('obj', getObj().a, objj.a)//1 2

 

2.类与this的引入

我们在面向对象时就说过,面向对象的一大特点就是将多个变量进行分类存在一个特定的对象中,但是,当多个对象又有相同特征的时候,我们怎么分类?这里就可以引入类的概念,比如上述的objj和getObj.那引入类的概念,就必须引入this指针.所以上述代码就可以变成

const getObj = function () {
  this.a = 1
  this.b = ['1', '2']
  this.c = function () {
    return 'name'
  }
}

 但是需要注意的是,引入类的概念之后.由于this指针的问题.需要引入new的概念.

const GetObj = function () {  /当使用this进行指向赋值的时候,可以视其函数为构造函数,函数名首字母必须大写
  this.a = 1
  this.b = ['1', '2']
  this.c = function () {
    console.log('pp', this)
  }
  this.c()
}
// let objj = GetObj()  不使用new会导致this指向报错
let objj1 = new GetObj() //objj1就是GetObj的一个实例
objj1.c()
console.log('obj', objj1)

 3.原型(prototype)

可以把原型看成类的传家宝,也就是说一个类的祖宗原型里放了个东西之后,所有的new过之后的实例都能拿到原型里的东西.

构造函数的每次实例化都相当于重新将其属性给实例,假如构造函数有一个a属性,那我每次实例化的时候,实例都有a属性,10个实例就存了10遍a,导致性能的浪费.那有没有一个办法,只存一遍,但是实例所有实例能调用呢?

这个时候就可以借由原型来实现,

const GetObj = function () {
  this.a = 1
  this.b = ['1', '2']
  this.c = function () {
    console.log('pp', this)
  }
  this.c()
}
GetObj.prototype.name = '构造函数' //给构造函数的原型注入一个name属性
// let objj = GetObj()  不适用new会导致this指向报错
let objj1 = new GetObj()
objj1.c()
console.log('obj', objj1, 'name', objj1.name)

 结果:objj1本身没有name属性,但是可以打印出来.

原因:是当你试图从实例中获取某个属性时,它自身没有则会沿着原型链去找,有就取出.所以可以在原型中存储使用较多的变量,比如在使用Vue.js开发时,将axios注入Vue的原型.

原型污染

现象:当频繁使用原型注入的时候可能会导致原型污染,在不知不觉的情况下修改了一些常用的构造函数原型导致,构造函数出现问题.比如修改了Function构造函数原型中的原有属性,实例化函数对象的时候可能就会出现功能上的问题.

解决方案:原型污染一个很大的原因就是同名变量的修改导致,那我们结合上面所说的方案可以想到解决方案.即,专门放一个函数在原型上去储存所有的变量,而不是一股脑全往原型上放.

const GetObj = function () {
  this.a = 1
  this.b = ['1', '2']
  this.c = function () {
    console.log('pp', this)
  }
  this.c()
}
GetObj.prototype.addFunction = function (name, f) {
  this[name] = f
}
// let objj = GetObj()  不适用new会导致this指向报错
let objj1 = new GetObj()
objj1.addFunction('getName', function (n) {
  console.log('name', n)
})
objj1.addFunction('getAge', function (n) {
  console.log('age', n)
})
objj1.getName('小明') // name 小明
objj1.getAge('18') // age 18

 这种方案,GetObj这个构造函数原型上只添加了一个addFunction,然后所有的后续添加的函数都绑定到了实例上,并没有污染GetObj的构造函数.当然,你也可以绑到实例的原型上

 

4.链式结构

jquery.js有个明显的特点,就是链式结构.那么,如果我需要多个函数一步调用需要怎么去操作呢?关键还是在this上面

const GetObj = function () {
  this.a = 1
  this.b = ['1', '2']
  this.c = function () {
    console.log('pp', this)
  }
  this.c()
}
GetObj.prototype.addFunction = function (name, f) {
  this[name] = f
}
// let objj = GetObj()  不适用new会导致this指向报错
let objj1 = new GetObj()
objj1.addFunction('getName', function (n) {
  console.log('name', n)
  return this  //返回了this,下一个函数就可以直接通过点语法使用
})
objj1.addFunction('getAge', function (n) {
  console.log('age', n)
  return this
})
objj1.getName('小明').getAge('18') // name 小明 age 18

 

原型在面向对象的编程思路中起了非常重要的作用,因为开发几乎都是依赖于对象.所以原型链的传递在变量或函数的储存起着至关重要的作用.

 

推荐阅读