OOP & 继承 & 闭包 & ES5对象扩展

创建对象及使用

  1. 创建对象
  2. 对对象进行描述
    (1) 有什么(属性)
    (2) 能做什么(方法)

对象的创建方式

  1. 字面量 {} 一般只用于创建单一对象

  2. new Object() 一般只用于创建单一对象
    (1)工厂模式:将参数传入函数内,函数内生成对象,给对象设置属性及方法(具体值来自传入 的参数),最后将对象通过return返回出去。

    (2)优点:解决了重复创建多个同一类型对象的问题

    (3)缺点:没有解决对象识别的问题(对象的类型都是Object)

  3. 自定义构造函数
    (1) 自定义构造函数与普通函数定义没有区别,唯一不同的是执行方式不同
      ① 函数名() ==>普通函数,若没有return,则返回值为undefined
      ② new 函数名() ==>构造函数
      ③ 任何函数,只要通过new操作符来调用,它就可以作为构造函数
      ④ 而任何构造函数,如果不通过new 操作符来调用,那它跟普通函数无区别

    (2) 执行new构造函数时的四大步骤:
      ①在构造函数内部声明了一个对象o
      ②将构造函数的this指向上面声明的对象o
      ③给this绑定属性及方法,相当于给对象o绑定
      ④将this对象返回出去

    (3) 优点:
      ①解决了重复创建多个同一类型对象的问题
      ②解决了对象的识别问题

    (4) 约定:构造函数定义时,首字母大写

this函数的当前对象

  1. 函数自执行:this指的是执行函数的对象
    (1) 一般省略了对象调用的函数,this指的是window
    (2) 对象.函数(),this指的是前面的对象

  2. 事件驱动函数:this指的是函数绑定的元素对象

  3. new 构造函数()的this指的就是当前实例对象

对象的组成部分

  1. 构造函数 function 函数名(){通过this定义属性及方法}

  2. 实例对象
    (1)new 构造函数() 产生的对象称为实例对象
    (2)实例对象能拷贝构造函数的所有属性及方法
    (3)实例对象可以使用其原型对象的所有方法

  3. 原型对象
    (1)原型对象都拥有一个constructor属性(构造器),指向构造函数
    (2)构造函数通过prototype属性指向原型对象
    (3)实例对象通过__proto__指向原型对象
    在这里插入图片描述

实际运用

  1. 解决方案:构造函数+原型对象
    (1) 使用构造函数添加私有属性
    (2) 使用原型对象添加共享方法

  2. 优点:
    (1) 实例对象都有自己的独有属性
    (2) 实例共享了原型中的方法,最大限度的节省了内存
    (3) 支持向构造函数传递参数

ES5对象扩展

  1. 属性特性

  2. Object.create(prototype)

以指定的原型创建对象

  1. Object.defineProperty(object, propertyname, descriptor)

对指定的对象的一个属性设置属性特性

  (1) 值属性
    ① configurable
      可配置性,控制着其描述的属性的修改,表示能否修改属性的特性
    ② enumerable
      可枚举性,表示能否通过for-in遍历得到属性
    ③ writable
      可写性,表示能否修改属性的值
    ④ value
      数据属性,表示属性的值。默认值为undefined

  (2) 访问器属性
    ① configurable
    ② enumerable
    ③ get

     在读取属性时调用的函数。只指定get则表示属性为只读属性。默认值为undefined

    ④set

     在写入属性时调用的函数。只指定set则表示属性为只写属性。默认值为undefined

  1. Object.defineProperties(object, descriptors)

     对指定的对象的一组属性设置属性特性

    //例:
    Object.defineProperty(obj,"gender",{value:"女",enumerable:false});
  1. Object.getOwnPropertyDescriptor(object, propertyname)

     返回属性特性

  1. Object.keys(object)

     返回对象所有可枚举属性的名称

  1. Object.getOwnPropertyNames(object)

     返回所有属性的名称(哪怕说是不能枚举的属性)

  1. 检测一个属性是存在于对象本身中
    (1) 对象.hasOwnProperty(属性)
    (2) 返回true,说明属性存在对象中
    (3) 返回false,说明属性不存在或在原型中

  2. 重置原型对象
    (1) 重置原型对象,可以一次性给原型对象添加多个方法,但切断了与原来原型对象的联系

function Popover(){}
Popover.prototype = {
    show:function(){},
    hide:function(){}
}
// 注意覆盖问题(系统的原型对象不建议重写)
// 注意识别问题(不可枚举性)
Object.defineProperty(Popover.prototype,"constructor",{value:"Popover",configurable:true})
  1. 内置构造函数的原型对象
    (1)使用内置原型可以给已有构造函数添加方法
      ①数组/字符串/数字等方法调用原理
      ②扩展内置方法
//演示:对象的内置函数及扩展内置函数
if(!Array.prototype.norepeat){
    Array.prototype.norepeat = function(){
        return Array.from(new Set(this)); // this:指向实例arr
	}
}

备注:

  • 通过.或者[]给对象添加的属性,拥有的属性特性默认值都为true(除了value为具体的值)
  • 拥有的属性特性默认值为false(除了value为具体的值)

原型链

  1. 实例对象与Object原型对象之间的链条称为原型链
  2. 原型模式的访问机制
    (1) 读取实例对象的属性时,先从实例对象本身开始搜索。如果在实例中找到了这个属性,则返 回该属性的值

    (2) 如果没有找到,则继续搜索实例的原型对象,如果在原型对象中找到了这个属性,则返回该 属性的值

    (3) 如果还是没找到,则向原型对象的原型对象查找,依此类推,直到Object的原型对象(最顶 层对象)

    (4) 如果再Object的原型对象中还搜索不到,则抛出错误
    ① 实例对象属性–>其原型对象–>原型对象的原型对象–…>Object的原型对象

继承

  1. 概念:继承是面向对象中一个非常重要的特征。指的是:子类继承父类的属性和方法

我们可以通过继承的方式, 在父类的属性和方法基础上, 让子类也拥有这些属性和方法, 并 可以扩展

  1. 继承的好处
    (1) 子类拥有父类所有的属性和方法(代码复用)
    (2) 子类可以扩展自己的属性和方法(更灵活)
    (3) 子类可以重写父类的方法

  2. 继承方式
    (1) 原型链继承
       ① 核心:拿父类实例来充当子类原型对象
       ② 缺点:无法操作父类构造函数的属性

   (2) 借用构造函数:
     ①父类的构造函数拿到子类的构造函数中,改变this的指向,同时执行该函数,记得传参
        解决了子类拷贝了父类的所有属性
        缺点:父类存在多余的属性

     ② call:
       格式:父类构造函数.call(子类实例,参数1,参数2,参数3…)
     ③apply:
       格式:父类构造函数.apply(子类实例,[参数1,参数2,参数3…])
       call与apply的唯一区别:传参方式不同,call多个参数,apply只有两个参数为数组

			aplly用法:借用方法
			var arr = [20,2,40,33,21,8,22,46,32]
			Math.max.apply(null,arr)

   (3) 组合式继承:原型链继承方法+借用构造函数拷贝属性
      ① 由于以上继承方法的缺点,实际开发中不可能单纯的只使用一种继承方法,而是利用它 们的优点,规避它们的缺点,所以就有了组合继承法(组合继承是最常用的继承模式)

      ② 继承属性:借用构造函数

      只在构造函数中定义属性

      ③ 继承方法:原型链继承

      把所有的方法写入对象

      ④ 缺点(原型链继承法的缺点)

      在原型对象中生成多余的属性
      多次执行父类构造函数

在这里插入图片描述
   (4) 原型式继承
      ① 核心:先创建了一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例
      解决了原型链继承法的缺点(不会生成多余的属性)

		function object(o){
		    function F(){}
		    F.prototype = o;
		    return new F();
		}
		子类的原型对象 = object(父类的原型对象)

      ② ES5版本的原型式继承:Object.create()

   (5) 寄生组合继承法(完美的继承方法)
      ① 核心
         继承属性:借用构造函数
         继承方法:原型式继承
在这里插入图片描述4. ES6中的继承
   (1) Class定义类
      ① ES6提供了更接近传统语言的写法,引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类
      ② 写在类里面的方法实际是给Animal.prototype添加方法
      ③ constructor方法是类的默认方法,相当于在构造函数内生成属性

		class Animal{
		constructor(jiaosheng,age){
		       	this.jiaosheng = jiaosheng;
		       	this.age = age;
		       };
		       eat(){
		         	console.log("吃");
		       }
		}

   (2) extends继承
      ① 子类继承了父类,在子类构造函数中必须调用super方法(借用构造函数)
      ② 子类的constructor方法没有调用super之前,不能使用this关键字,否则报错,而放在super方法之后就是正确的

   (3) 静态方法
      ① 如果在一个方法前,加上static关键字,这就称为"静态方法"
        静态方法方法不会被实例继承,而是直接通过类来调用Person.getInfo()
        父类的静态方法,可以被子类继承Man.getInfo()

function的方法

  1. bind()    改变this的指向,返回新函数
    call()    改变this的指向,并且执行函数
    apply()   改变this的指向,并且执行函数,函数的参数为数组
  2. call()的使用
    ① 借用其他构造函数的原型对象中定义的方法
//例:
var btn = document.querySelectorAll(".btn");//类数组
var arr = Array.prototype.map.call(btn,function(item){
  return item.value;
})

   ② 自己本身存在某个方法,但想使用原型链上其他原型对象的某个方法

//例:
function getType(obj){
	return Object.prototype.toString.call(obj).slice(8,-1);
}
  1. apply()的使用
    ① 利用apply方法,可以将参数转成数组传入
//例:
var arr = [1,2,3,10,40,100,30,2,4];
var res = Math.max.apply(null,arr);
console.log(res);

闭包

  1. 闭包是这样一种机制: 函数嵌套函数, 内部函数可以引用外部函数的参数和变量,参数和变量不 会被垃圾回收机制所收回

  2. 优缺点
    (1) 可以让一个变量长期驻扎在内存当中不被释放。过度使用闭包,会占用过多的内存,造成性 能问题
    (2) 闭包内的变量不会被函数外使用到
    (3) 私有成员的存在, 无法被外部调用, 只可以自己内部使用

  3. 结论
    (1) 闭包是指有权访问另一函数作用域中的变量的函数

    (2) 闭包,可以访问函数内部的局部变量,并让其长期驻留内存

    (3) 由于闭包会携带包含它的作用域(运行环境),因此会比其他函数占用更多内存,过度使用闭 包可能会造成性能问题

猜你喜欢

转载自blog.csdn.net/qq_42827425/article/details/88580350