五大模式(原型模式)

单例模式

  • 把描述同一个事物特征的信息进行分类归组,放到同一个命名空间下(减少全局变量的污染)
        var name = "邢";
        var age = 21;
        var sex = "男";
        
        var name = "刘";
        var age = 21;
        var sex = "男";
        
		//
        var xing = {
            name: "邢",
            age: 21,
            sex: "男"
        };
        var liu = {
            name: "刘",
            age: 21,
            sex: "男"
        };
        //描述同一个对象的所有属性放在对象中,避免了全局变量的干扰,"单例模式"
        var timer = 1;
        var xing = {
            bar: function () {

            },
            fn: function () {

            }
        };
        // var timer = 1;
        var liu = {
            bar: function () {

            }
        };  
-----------------------------------------------------
			 var name = 'erYa';
       var age = 18;

       var name = 'jinYu';
       var age = 22;

       let person1 = {
           name: 'erYa',
           age:18
       };
       let person2 = {
           name: 'jinYu',
           age: 22
       }

高级单例模式

        //高级单例模式
        //jquery的源码的外层是采用了高级单例模式
        var utils = (function () {
            var timer = 1;
            var num = 100;
            return {
                bar: function () {

                },
                fn: function () {

                }
            }
        })()
        //与单例模式相比,想使用同一变量,可以避免污染全局
--------------------------------------------------
		let person1 = (function(){
        let fn = function(){};
        let name= 'erYa';
        return {
            name:name,
            age:18,
            fn:fn
        }
    })()


        let person2 = (function(){
        let fn = function(){};
        let name= 'jinYu';
        let age = 18;
        return {
            name:name,
   
            fn:fn
        }
    })()

    let person3 = function(){
        let name= 'jinYu';
        let age = 26
        return {
            name:name,
            fn:fn
        }
    }
    console.log(person3) // 这是一个函数,不是person3的个人信息

工厂模式

  • 如果用单例模式去写很多个person就会变得很麻烦,就有了工厂模式 特点:批量生产
  • 把实现相同功能的代码封装到函数里,以后想运行这个方法,就直接执行这个函数就好了
  • 高内聚:提高代码的复用率
  • 低耦合:减少页面的重复代码
        //工厂模式: 函数的封装
        function createStudent(name, age) {
            var obj = {}
            obj.name = name;
            obj.age = age;
            return obj;
        };
        var xing = createStudent('邢', 21);
        var liu = createStudent('刘', 21);

对象前言

javaScript
面向对象: 基于对象
对象: 万物皆对象
类: 指具有相同特征的一类事物
实例:具体的一个事物:

  • 内置类 : Number String Boolean Null Undefined Object Array RegExp Date …
  • 类一定是个函数,但函数不一定是类;

面向对象

  • 把抽象的对象按照特点进行分类(大类/小类),把类的公共特征进行提取和封装,放到对应的类别中
  • 类就是对对象的一种细分,和公共部分的抽取
  • 在类中具体派生出来的具体事物就是类的实例,而且实例拥有自己私有的特征,还拥有所属类上的特征
  • 我们研究面向对象,其实就是研究对象、类、实例之间的关系和各自的知识点

在这里插入图片描述

构造函数模式(构造自定义类)

构造函数解决了实例的私有属性

  • var f=new Fn();
  • new 操作符——把 new 放在函数的前面,函数执行
  • 构造函数就是让我们创建自定义类
  • new 函数执行 叫做构造函数运行模式,此时的Fn就是Fn类(构造函数),函数执行之后的返回结果就是一个对象,叫做实例对象(f就是Fn的实例)
  • 类就是函数数据类型的
  • 实例是对象数据类型
  • 构造函数中的this指向当前实例
    • 如果这个函数需要参数,那么这个需要有小括号,如果不需要参数,那么小括号可以省略
  • new后面的函数就是构造函数,也叫类;那么通过new函数得到的返回值就叫实例;实例是构造函数new出来的;

> 钩子函数:在初始化实例时会默认调用原型上的一些方法,那么这些方法就是钩子函数

运行原理

  • 1、在代码执行之前,函数中会首先默认创建一个空对象 {}
  • 2、让当前函数里的this指向这个对象
  • 3、代码执行
  • 4、默认return 这个对象

let f1 = new Fn(‘erYa’, 18);
//原型三句话
//f1就是Fn的实例
//fn就是函数,也是自定义(Fn)类
//f1就是实例化的对象

//let f3 = new Fn;
如果构造函数不传实参,可以省略执行小括号
new 函数执行 叫做构造函数运行模式,此时的Fn就是Fn类(构造函数),函数执行之后的返回结果就是一个对象,叫做实例对象(f就是Fn的实例)

          function fn() {
            // var obj = {};
            // this = obj;
            this.name = 100;
            // return this;

        };
        var f = new fn;
        fn();
        console.log(f);

		/* 
       刚才那些都是js的内置类,但是我们也可以自己去自定义一些类
       */

        //   构造函数(构造自定义类)

         function Fn(name, age) {
            /* 
            形成私有作用域
            形参赋值
            变量提升
            1、默认生成一个空对象 {}
            2、让函数里的this指向这个对象
            3、代码执行
            4、默认return 这个对象
            */
            this.name = name; // 给this增加键值对
            this.age = age // 给this增加键值对
            this.say = function(){}
            return {}
        }

        // new: 他是js里的关键字
        Fn() // 普通函数运行

        let f = new Fn()  // {}
        let f1 = new Fn('erYa', 18);
		//原型三句话
        //f1就是Fn的实例
    	//fn就是函数,也是自定义(Fn)类
		//f1就是实例化的对象
		
        let f2 = new Fn('jinYu', 18);
        let f3 = new Fn;//如果构造函数不传实参,可以省略小括号
        let f4 = Fn;
        console.log(f)
        console.log(f1)
        console.log(f2)
        console.log(f1.age === f2.age)
        console.log(f1.say === f2.say)
        console.log(f3)
        console.log(f4)
        // new 函数执行 叫做构造函数运行模式,此时的Fn就是Fn类(构造函数),函数执行之后的返回结果就是一个对象,叫做实例对象(f就是Fn的实例)

构造函数和普通函数的不同

JS为了区分构造函数和普通函数,一般将构造函数首字母大写;

  • 运行上的不同
    • 普通函数–>形成私有作用域–>形参赋值–>变量提升–>代码执行–>作用域是否销毁
    • 构造函数–>形成私有作用域–>形参赋值–>变量提升–>默认生成一个对象–>把this指向这对象–>代码执行–>默认把这个对象return出去–>作用域是否销毁
  • 执行上的不同
    • 构造函数如果不传实参,可以不加小括号
  • 构造函数如果手动return一个基本数据值,不能改变人家的返回值,但是手动return引用数据类型,可以改变构造函数的返回值,此时return的东西已经不是当前类的实例了【所以不要轻易修改构造函数的返回值】
        function Fn(n) {
            let m = 10;
            this.total = m + n;
            this.say = function () {
                console.log(this.total)
            }
        }
        let f1 = new Fn(10);
        let f2 = new Fn(20);
        console.log(f1.n)//undefined
        console.log(f2.m)//undefined
        console.log(f1.total)//20
        f2.say()//30
        console.log(f1 === f2)//false

创建实例的方式

  • 字面量创建实例的方式
		let num = 1;
		let str = 'w';
  • 构造函数创建实例的方式
        // 实例创建
        var num = new Number(1); 
        console.log(typeof num);// "object"
        console.log(num instanceof Number);//true
        console.log(num);//Number {1}
        console.log(num+1);//2
        console.log(num == 1 )//比较true,绝对比较就是false
        //  如果是数字并且只有一个参数,代表数组的length;如果大于等于两个,那么是数组的每一项;
        var  arr = new Array(100,200);
        console.log(arr);//[100, 200]
        console.log(typeof Array);// "function"
        var arr = new Array("a")
        console.log(arr);//["a"]  

------------------------------------------------------------------------
		let ss = new Number(1)
        console.log(ss)
        console.log((1).toFixed(2)) // '1.00'  把数字转换为字符串,保留指定位小数
        console.log(ss.toFixed(2)) // '1.00'
        let w = new String(3) // 创建一个字符串的实例
        console.log(w)
        console.log(w.substr) // f//.substr字符串里的方法
        console.log(w instanceof String) // true

检测当前实例是否属于某个类

  • 使用instanceof是检测当前实例是否属于某个类
  • 实例 instanceof 类,如果实例是属于这个类,那就返回true,反之就是false
  • 通过字面量方式创建的基本数据类型值不是一个标准的实例,不能使用instanceof 进行检测;引用数据类型创建的就是一个标准的实例,可以使用instanceof来进行检测;

局限性

  • instanceof不能检测基本数据类型,只能检测引用数据类型的值
        // 字面量方式创建变量
		// var num = 1;// num 是Number的一个实例吗?是
        // console.log(num  instanceof Number);// false
        // var  obj = {};
        // console.log(obj instanceof Object);// true
        // var ary = [];
        // console.log(ary instanceof Array);// true
--------------------------------------------
		function Fn(name, age){
            this.name = name;
            this.age = age;
        }
        let f1 = new Fn;
        console.log(f1 instanceof Fn) // true
        console.log(1 instanceof Fn) // false

给实例的类封装公共方法需要注意的几点

  • 你自己封装的方法不能与人家内置的方法同名
  • 给你自己的方法加前缀
	//去重
   function myUnique(){
        console.log(this)
        let obj = {};
        for (var i = 0; i < this.length; i++) {
            if(obj[this[i]] !== undefined){
                this[i] = this[this.length-1];
                this.length--;
                i--;
                continue;
            }
            obj[this[i]] = this[i]
        }
    }
    Array.prototype.myUnique = myUnique;
    console.log(ary.myUnique().sort().reverse().slice(1,3))
    // 内置类的链式扩展:如果你想用链式调用,前提是你的方法的返回值必须是当前类的实例

原型模式

  • 原型模式: 构造函数解决了对象实例中私有属性的问题,原有模式解决了对象实例中公有属性的问题;如果将实例的私有属性放到了公有属性上,减少了堆内存的开辟;

原型解决了实例的公有属性

  • 每一个函数(普通函数,构造函数)都天生自带一个prototype属性,属性值是一个对象,它里面存储的是实例的公有属性(原型)
  • 每一个原型都天生自带一个constructor属性,其属性值指向当前原型所属的类
  • 每一个对象都天生自带一个__proto__属性,其属性值指向当前实例所属类的原型
    在这里插入图片描述

原型链

  • 在对象里查找-一个属性,先看自己私有的有没有,如果自己没有,就通过__ proto__ 属性找 到当前所属类的原型上,如果原型上有,就直接用,如果没有,继续通过原型的__ proto__ 继续往所属类的原型上找,直到找到Object类的原型上找,如果还没有,就是undefined, 这种级- 一级一级向上查找就会形成原型链
       function Fn(x, y) {
            this.x = x;
            this.y = y;
            this.getX = function () {
                console.log(this.x);
            }
        }

        Fn.prototype.getX = function () {
            console.log(this.x);
        }
        Fn.prototype.getY = function () {
            console.log(this.y);
        }
        var f1 = new Fn(100, 200);
        f1.__proto__.a = 300;
        var f2 = new Fn(100, 200);
        console.log(f1.getX == f2.getX);//false
        console.log(f1.getY == f2.getY);//true
        console.log(f1.x == f2.x);//true
        console.log(Fn.prototype.getX === f1.getX);//false
        console.log(Fn.prototype.getX === f1.__proto__.getX);//true
        f1.getX()//100
        Fn.prototype.getX();// undefined
        f2.__proto__.getX();//undefined
        Fn.prototype.getY();//undefined
        console.log(f1.a);//300
        console.log(f1.toString);//ƒ toString() { [native code] }

原型:

  • window 是全局作用域中一个大的对象;window是Object的一个实例;
  • Function是所有函数数据类型的基类(包括自己)
  • Object是所有对象的基类
  • 所有的函数都有prototype和__proto__属性
  • Object的原型的__proto__执行的是自己,js认为自己指向自己没有意义,就规定为null
  • 所有的函数数据类型(普通的函数、类【内置类,自定义类】)都是Function的一个实例,Function和Object都是Function的一个实例,那Object类的__proto__指向Function的原型
  • 所有函数都是Function的实例, 那Function也是函数, 那他的__proto__指向的是自己的原型。它的原型是一个匿名函数,但是也是对象,正常使用即可
    • 当函数作为对象时,有length和name属性
      • length:代表形参的个数,name:代表函数的名字
  • 如果一个对象你不知道谁构出来的, 那他的__proto__就指向Object内置类的原型( 所有类的原型都指向Object内置类的原型)
  • 函数的三种角色: 普通函数 构造函数 普通对象
  • 在正式场合函数是以函数的身份出场的( 这是人家的主角色)
  • 类的特点: 多态、 继承和封装
  • 给Fn的原型新增键值对;这就是原型扩展;

QQ图片20191122182804.jpg

原型图

  • 原型链:单向不可逆

原型图.png

原型重定向

  • 内置类的原型不能被重定向
  • 可以覆盖内置类原型上的方法
  • 用新的空间地址覆盖Fn原有的空间地址;会导致constructor的丢失;
  • 重定向例子
        function Fn() {
            this.x = 100;
            this.y = 100;
        }
        Fn.prototype.getX = function () {
            console.log(this.x)
        }
        let f1 = new Fn;
        Fn.prototype = {
            getY: function () {
                console.log(this.y)
            }
        };
        let f2 = new Fn;
        
        console.log(f1.getX)
        console.log(f2.getX)
        console.log(f1.constructor)
        console.log(f2.constructor)
        console.log(Fn.prototype)

原型重定向.png

阿里面试题(原型链、运算符优先级)

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>

<body>
    <script>
        function Foo() {
            getName = function () {
                console.log(1);
            };
            return this;
        }
        Foo.getName = function () {
            console.log(2);
        };
        Foo.prototype.getName = function () {
            console.log(3);
        };
        var getName = function () {
            console.log(4);
        };

        function getName() {
            console.log(5);
        }

        Foo.getName(); //2
        getName(); //5//4
        Foo().getName(); //2//1
        getName(); //4//1
        new Foo.getName(); //2
        new Foo().getName(); //1//3
        new new Foo().getName(); //1//3

        /* 
        1、成员访问:寻找对象里的属性名所对应的属性值就是成员访问(19)
            Fn.aa
        2、new(带参数列表):就是构造函数执行有括号(19)
        3、new(无参数列表):就是构造函数执行没有括号(18)
        优先级一样,从左到右运算
        
         */
    </script>
</body>

</html>

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章