23 js高级-构造函数和原型

158 阅读10分钟

构造函数

类是对象的模板,对象是类的实例

而在es6之前,js并没有引入类的概念

es6 全称为 ECMAScript6.0 , 在es6之前,对象不是基于类创建的,而是一种称为构建函数的特殊函数来定义和它们的特征

也就是说,在es6之前,创建对象是利用构造函数创建的,在es6之后,利用class 类 来创建对象

创建

1、利用构造函数创建

image.png

构造函数是一种特殊的函数,主要用来初始化对象; 就是为对象成员变量赋初始值

构造函数与new一起使用,我们把对象中的一些公共的属性和方法抽取出来,然后封装到这个函数里面

使用构造函数注意事项

在JS中,使用构造函数时要注意

1、首字母大写

2、构造函数要与new 一起使用才有意义


new 在执行时会做的四件事:

1、在内存中创建一个空对象

2、让this指向这个空对象

3、执行构造函数里面的代码,给这个新对象添加属性和方法

4、返回这个新对象(所以构造函数里面不需要return)

实例成员和静态成员

实例成员

实例成员 : 在构造函数内部通过this添加的,访问的时候只能通过实例化对象来访问

image.png

静态成员

静态成员 : 在构造函数本身上面添加的,访问的时候,只能通过构造函数本身来访问

image.png

原型 prototype

为什么需要原型?

用构造函数来创建对象的方法很好用,但是存在浪费内存的问题

image.png

我们希望所有的对象使用同一个函数, 这样就比较节省内存,那么我们可以使用原型对象

如何去使用

构造函数通过原型对象分配的函数是所有对象所共享的 。

js规定,每一个构造函数都有一个prototype属性,指向另一个对象;这个prototype就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有

image.png image.png

我们可以把那些不变的方法,直接定义在prototype对象上,这样所有对象的实例就都可以共享这些方法了。

image.png

那么此时dk的sing和joshua的sing就相等了

image.png

总结原型

  • 原型 就是一个对象,所以我们也称prototype为原型对象

  • 原型的作用是 : 共享方法

  • 一般情况下,我们公共属性定义到构造函数中,公共的方法放到原型对象上

对象原型 _ proto _ (每边2个_ )

定义

对象都会有一个属性 _ proto _ ,指向构造函数的prototype原型对象,之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有 _ _ proto _ _ 原型的存在

(对象原型 _ _ proto_ _ 指向的是原型对象)

image.png

image.png
  • 对象中的_ _ proto_ _ 和 构造函数的prototype的一样的

image.png

方法查找规则

  • 方法查找规则 : 首先看对象是否有这个方法,若有就执行这个对象上的方法;若没有,就去构造函数原型对象prototype 上去寻找

125cff160bb717349a2c856f321a77b.jpg

_ proto _ 对象原型的意义

  • _ proto _ 对象原型的意义就在于为对象的查找机制提供一个方法,或者说一条路线,但它是一个非标准的,因此在实际开发中,不可以使用这个属性,它只是内部指向原型对象prototype

constructor 构造函数

定义

对象原型__proto__ 和 构造函数原型对象prototype里面都有一个属性 constructor属性,constructor我们称为构造函数,因为它指回构造函数本身

image.png

image.png

使用

constructor主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数

image.png

但输出 constructor 时,指向的却不是 原来的构造函数

image.png

image.png

这样是因为原来的构造函数的prototype是添加方法,而现在这个是 把新添加的多个方法赋值给prototype ,所以里面原有的 constructor不存在了。

那么我们需要在prototype中重新声明一下

  • 那么若我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数
image.png

那么此时的constructor里面就指向了原来的构造函数

image.png

构造函数、实例和原型对象的三者之间的关系

dc9d566e4c994bd58a5b0e50b2f9ab2.jpg

原型链

图解

25c361e5ae67337788a79373cc44443.jpg

js的成员查找机制(规则)

  1. 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性

  2. 若没有就去找它的原型 (也就是对象._ proto_ 指向的是 prototype原型对象)

  3. 若还没有,就去查找原型对象的__proto__ (也就是Object的原型对象)

  4. 以此类推一直找到Object.prototype._ proto_ 为止(null)

原型对象的this指向

1、在构造函数中的this ,指向的是 实例对象

image.png

2、在构造函数的原型对象函数里面的this,指向的是 实例对象

image.png

注 : 必须调用了,才能知道此时的this 指向谁

image.png

利用原型对象扩展内置对象

可以通过原型对象,将原来的内置对象进行扩展自定义的方法。比如给数组增加自定义求偶数和的功能

image.png

注 : script执行代码是从上到下的,所以先添加方法,再创建实例对象

还可以利用 new Array来创建数组

image.png

错误写法:

image.png

数组和字符串内置对象不可以给原型对象覆盖操作Array.prototype = {} ,只能是 Array.prototype.xxx = function(){} 的方法

继承

es6之前没有提供 extends继承,我们可以通过 构造函数 + 原型对象 模拟实现继承,被称为组合继承

call()

作用 : 调用函数 和 改变this指向

调用函数

构造函数名.call()

image.png

改变this指向

call() 可以改变 函数里面的this指向

() 里面无参数

image.png

() 里面有参数

image.png

利用call借用构造函数继承父类型属性

因为es6之前没有class类,所以无法使用extends来继承

1、定义父构造函数

image.png

2、定义子构造函数

image.png

3、此时 想要继承父构造函数的属性 , 那么就利用call方法

image.png

4、实例化子构造函数

image.png

继承 : 子继承父,子需要继承父里面的方法属性,

        那就在子构造函数中 把父构造函数的 this 指向指向子构造函数

        所以,此时的 Father 里面的 this 指向的是子构造函数
         
        然后把 uname 和 age 参数传进去

利用call在原型对象继承方法

1、定义父构造函数

image.png

2、在父构造函数中把共用方法写在原型上

image.png

3、定义子构造函数 image.png

4、在子构造函数中,要继承父构造函数的方法,所以要利用call() 调用父构造函数

image.png

5、(错误写法使用 Son.prototype = Father.prototype ,就是把父构造函数中的原型对象给子构造函数的原型对象(子构造函数原型对象 指向 父构造函数的原型对象),但此时子构造函数单独拥有的原型对象也给父构造函数了

image.png

图解 :

b816fdd95f622064325ae4359bd7a48.jpg

6、正确使用方法

image.png

7、此时的constructor函数

image.png

image.png

8、指回Son的构造函数

image.png

图解 :

826428dedd0e052adf90b473394cb10.jpg

类的本质

es6之前 ,构造函数

es6之前,通过 构造函数 + 原型 实现面向对象编程

  • 构造函数的特点

    1、构造函数有原型对象 prototype

    2、构造函数原型对象prototype里面有constructor,指向构造函数本身

    3、构造函数可以通过原型对象添加方法

    4、构造函数创建的实例对象有__proto__ 原型指向 构造函数的原型对象

es6之后,类class

定义

类的本质是一个函数,可以简单的认为类就是构造函数的另外一种写法

image.png

特点

  • 特点 :

    1、类里面有原型对象 prototype

    image.png

    2、类里面的prototype也有constructor,指向的是类本身

    image.png

    3、类也可以通过原型对象添加方法

    image.png

    4、类创建的实例对象__proto__ 原型 会指向 类的原型对象

    image.png

ES5中的新增方法

遍历(迭代)数组

forEach

forEach 会遍历数组里面的元素

array.forEach(function(currentValue,index,arr){})

1、currentValue : 数组当前项的值
2、index : 数组当前项的索引
3、arr : 数组对象本身
image.png

map

会返回一个新的数组,map是对数组里面的每一个元素都进行相同的操作

array.map(function(currentValue,index,arr){})
image.png

image.png

filter

会返回一个新的数组,对旧数组中里面的元素进行检查是否符合条件;主要用于筛选数组

array.filter(function(currentValue,index,arr){})

image.png

image.png

some

是检查数组中的元素是否满足指定条件 ; 也就是说,查找数组中是否有满足指定条件的元素

array.some(function(currentValue,index,arr){})

1、它返回的是布尔值,若查找到这个元素,就返回true;若找不到就返回false

2、若找到第一个满足条件的元素,那么就终止循环,不需要继续查找

3、currentValue : 数组当前项的值

4、index : 数组当前项的索引

5、arr : 数组对象本身

image.png

image.png

filter 和 some 的区别

filter 和some 都是进行筛选的 , 不过filter返回的是一个新的数组,里面包含的也是所有满足条件的元素

some 返回的是布尔值,找到就true ; 没有找到就是false ; 而且 some 是找到第一个就不会再继续查找下一个了

every

检测数组中的所有元素是否都满足条件,若有一项不满足,那么返回false ; 都满足则就返回true

array.every(function(currentValue,index,arr){})


2、返回的是布尔值

3、返回一个新的数组

4、不会对空数组进行检测

image.png

因为不会对空数组进行检测,所以 当every遇到空数组的时候,就会直接返回true

image.png

every 和 some 的区别

every 和 some 返回的都是布尔值 ; 不过 every是判断整个数组里面的值是否满足条件,若都满足则返回true , 否则返回false 。

而 some 是 判断数组中是否有满足条件的值,若有则返回true ,没有就false

some 和 forEach 区别

image.png

some 遇到 return true 则终止迭代,迭代效率更高 ; 而 forEach 和 filter 不一样 ,里面写return true 不会终止迭代

字符串

trim 去除字符串两侧空白字符

trim() 

1、去除字符串两侧的空白字符 , 不会去除中间的

2、会生成一个新的字符串,所以要接收这个新字符串


image.png

Object.defineProperty(obj,prop,descriptor)

作用 : 定义对象中新属性 或 修改原有的属性

1、obj : 必需 。 目标对象

2、prop : 必需 。 需定义或修改的属性的名字

3descriptor(说明) : 必需 。 目标属性所用于的特性 ; 以对象的形式 {} 书写 ,

若对象中没有该属性则进行添加 ; 若有该属性则进行修改

descriptor 里面的 value

设置属性的值 。 默认 undefined

image.png

image.png

image.png

descriptor 里面的 writable

值是否可以重写 。 true | false 默认false

image.png

image.png

descriptor 里面的 enumerable

目标属性是否可以被枚举 。 true | false 默认false

image.png

image.png

注 : 前面创建的num无法遍历出来,是因为 enumerable 默认就是false 无法遍历

descriptor 里面的 configurable

不允许修改第三个属性里面的特性

image.png

若修改会报错

遍历对象 Object.keys(对象名)

返回的是由属性名组成的数组

1、和 for in 差不多,都是遍历对象的

2、不过,它返回的是一个 由属性名组成的数组 
 
3、好处是 : 因为返回的是一个数组,所以可以直接使用forEach 进行遍历

image.png

image.png