美文网首页
JS原型的学法

JS原型的学法

作者: 流着万条永远的河 | 来源:发表于2017-11-06 01:02 被阅读0次

    从初学JS就有比较片面的学法,比较定死的一些概念啊,结论之类的,只是为了学习每个东东的各自的基本作用的,不需要纠结它是怎么来的,只需要知道它能干吗就可以的,小学里最小的数是0,对的。初中就是错的。为什么,不是因为绝对的错了,是因为应用环境错的,平台换了哦。说这些,意思就是看山不是山的时候到了。
    JS的数据类型,数字,字符串,布尔,对象,null,undifined。然后,我们用typeof去检验,嗯有些历史遗留的问题,函数怎么是对象,为啥检测是function?null为啥是对象?需要跟它们深入地谈谈人生啦。

    对象

    什么?你个攻城狮,竟然还么有对象?丢人不,new一个出来,来来来,,,
    对象是什么玩意儿?
    我是非科班出身,连c都没学过,我只写自己的理解和被洗脑后的感悟——对象,就是我要针对的目标,这个目标在JS里啊,是数据和数据属性,数据方法这些东东臃肿地一个封装集合。别看我平时用的时候,一个标签可以表现它的存在感,它只是那个标签吗?为啥我可以设置它的属性,它的内容,它的事件,大小,它的一些操作方法function。包括数组,内置的方法,遍历等,说到函数,函数为什么声明前置,为什么会有作用域,这不是属性规则吗?包括连字符串,数字不是都有属性方法吗?
    对,有句话说,JS里万物基于对象!因为我们把各个研究对象内置了属性和方法,便于操作,否则,单单只是变量,要操作的话,每次都要写个函数,声明个变量作为它的属性,再操作,,,烦不烦啊,郭德纲说过,你死不死啊,,,
    对,我们学JS时,对象浅显地理解为key-value的值的合体。不需要研究太深,还是那句话,越实在暴露出来的,越是最基本的,这里的对象是面向对象,就是目标数据和这目标的规定的变量包括内置的参数我们叫属性,长宽高等等吧,以及对这数据的操作方法了,这是改变你世界观的一个意思,不要太肤浅,要知道一件事的源头,这样更有利于干什么?有利于我们学习JS的设计思想和攻城狮的本身品质。虽然我没学到前端工程化,但是我就觉得完成对对象的封装就有工程化的感觉了,而且确实会有很多福利,比如有些内置的方法只在某些对象身上,我想调用这个方法,否则我还要自己写,但是数据不是符合的,怎么办?那必须要可以啊,否则这语言也太low了,效率太低了。下面会讲到的。

    思路就是,把功能集成一个整体,不去考虑内部功能怎么实现的,会调用就行了,更简洁,可控。

    构造对象

    字面量的模式就是不能复用代码。复用代码怎么办?

    function createobj(ni,age){
      var obj = {
        ni:ni,
        age:age,
        printName:function(){
          console.log(this.ni)
        }
      }
      return obj
    }
    var obj1 = createobj('haha',26)
    obj1.printName()   //'haha'
    

    我的理想很丰满,如果给我一个房子的户型图,我能做出一百间这样类的房子,现实,嗯也是可以的。
    构造函数:

    function people(name,age){
      this.name = name
      this.age = age
      console.log(this)
    }
    people('haha',26)   //haha
    全局变量相当于window的属性,这个函数声明是全局变量,
    this就是指window。不是我想要的结果啊。
    如何调用?
    var obj1 = new people('li',20)
    var obj2 = new people('wu',20)
    

    都是people类的对象了。

    new 的过程

    new运算符接受一个函数F和其参数:new F(arguments...)。三步:
    1、创建类的实例,把一个空的对象的proto属性设为F.prototype。
    2、 初始化实例,F被传入参数并调用,关键字this被设为该实例。
    3、返回实例。
    obj1是空对象,啥都没有,它的proto指向这个函数声明时的prototype属性指向的那个对象。它再执行这个people函数,执行时,this指向obj1这个空对象。而函数执行时,给this赋值,就是给obj1增加属性,增加好了,就返回这个已经不是空的对象给obj1,所以obj1就是那个对象有三个属性。

    function people(name,age){
     //var  this ={}
      this.name = name
      this.age = age
     //return this
    }
    如果是要阻碍一下流程:
    function people(name,age){
     //var  this ={}
      this.name = name
      this.age = age
     //return 2  //改了后,没有影响,没阻碍,基本类型,浏览器默认无效
    }
    如果是,,,
    function people(name,age){
     //var  this ={}
      this.name = name
      this.age = age
     //return {a:1}     //这就有问题了。因为都是对象,就覆盖了
    }
    不需要return的
    

    instanceof

    操作符,判断对象是否为某个类型的实例。

    obj1 instanceof people
    //true
    obj2 instanceof people
    //true
    

    现在先说一句,任何的一个对象怎么来的,它的属性方法怎么来的,都是由一个函数创造出来的,对象有个属性叫constructor。
    现在构造函数,可以解决我们创建复用的难题了,但是里面的那个prototype是什么鬼??


    任何函数只要声明了,它就有一个prototype属性,这个属性的值对应到了一个对象,这个对象里有一个constructor,它指向这个声明的函数,还有一个proto
    有什么用呢?既然是对象,我们就可以写入方法属性,改成自己需要的图纸,再new出来一堆房子。这些房子都有一个属性proto,它也指向了图纸的prototype指向的那个对象,公用的,一般函数公用最好了。
    例子:
    function people(name,age){
      this.name = name
      this.age = age
      console.log(this.name)
    }
    people('haha',26)
    
    people.prototype.sayage = function(){
      console.log('hahaha')
      
    }
    people.prototype.sayage()
    var obj1 = new people('li',20)
    var obj2 = new people('wu',20)
    obj1.__proto__.sayage()
    

    如图:



    一般,不会这样写上proto的,因为直接写成obj1.sayage就可以的,因为它会先从自身属性找,找不到就从proto找。
    经验之谈:
    所有的实例都可以访问那个prototype,prototype是公用的,可以把公用的方法属性放到它里面。
    prototype是函数的属性,只要声明就有。
    __proto__是函数创建对象的时候,对象上有它。
    

    对于基本类型是对象的说法

    基本类型也是对象,因为有自己的属性方法的,但是我们作为应用层次,要严谨,要认为不是对象才去应用,因为不能随便给它增加删除属性方法,加了有时候也起不了作用的,起不了作用,也就不是对象了,看问题是相对的。
    下面原型就来了,,,

    对象与原型

    刷完对象的世界观,我们来想想那些开发JS的攻城狮们的套路啊,他们是设计了一个个的对象封装了,比如数组有个数组的封装组件,集成了所有数组的规则和应用基本方法,他们是开发人员,我们是应用层次啊,他们想让我们效率更高更好,比如,我们var了一个[],就能操作它,用push,shift等等方法,还可以查它的length,用下标找到对应的值,,,多方便!我没有自己去写push的函数吧,我不用关心怎么输入一个[].length就可以知道它的长度了。我会用就行了。现在我们要做什么?
    为啥嘞?这里的逻辑是什么?
    我们只是声明了某个类型的变量,然后这个变量就有这个类型的属性和方法了,并且根据JS的设计尿性,属性可以设置,那就可以创造,包括方法。
    在操作DOM时用的太多了,事实是必须的,之前写项目时,用封装,写成对象那样,里面的创造的很多变量就是对象的属性嘛。那我们可以创造对象的方法么?告诉你没毛病!
    这个中间肯定是有些规则,有些设定好的套路的,好比,规定[]就是数组。
    我们先验证一下,


    找到arr的constructor是f Array(),然后打开这个函数的prototype跟数组的proto比较一样吗。
    所以这个arr的方法源于它祖宗的prototype指向的对象。

    首先,开发人员们肯定根据数据类型集成对象,不会为了一个数组开发成两个三个多个面向数组的对象的,到时候用哪个,并且集成了,所有声明的数组都可以用,为什么非要分情况?而且都是只要是数组,你就可以拥有一堆同样的方法属性。这里我把这个源头的封装的对象叫它们祖宗。那这祖宗跟它后代如何查到互相关联?这里就要引入原型了!

    原型

    原型的思想就好比是模具,后面生产的产品跟它差不多,至少模具有的,产品都有,至于产品要不要深加工再出现模具没有的或者改变些神马,跟模具没关系了,关键是现在产品已经有了模具的特性了,不用我再手把手从头做起了。
    有大神,在这里引入了class的概念,类概念!类就是对象的抽象描述,对象是类的实例,反正那个祖宗,我们是看不到的,只能看到同类的后代怎样怎样。正好可以引出继承这一概念了,其实一说继承,就有父子关系了,可以说是子类继承父类的。在代码层次,就是新生产出的变量的方法和属性的集合的那个栈的指针指向哪里,指针从哪里赋值得到的。


    arr.valueOf()在Array.prototype里没有啊,可是没报错啊,,,
    再看看Array函数的proto里看看 ,有了,,,
    规定父对象也可以成为子对象的原型。每个对象都有一个原型,每个原型也是对象,也有自己的原型,从而形成原型链。

    因为访问对象的属性或者方法时,在自己身上没找到,就去自己的(父亲)原型prototype上找,再找不到,再去(父亲)原型的proto上找,再找不到,再去父亲的原型(爷爷)的prototype找,再找不到就到爷爷的proto,,,顺着原型链攀爬,直到找到或者到它们祖宗也没有。
    规定:

    1 js是基于原型的语言中的prototype
    2 每个对象都有自己的原型中的prototype
    3 每个函数都有一个原型属性中的prototype
    

    prototype是什么?上面说过了,现在再直白点,创造者有prototype,指向一个对象。
    生成者的proto的指针复制了创造者的prototype的内容。
    因为身份不一样,就算是同样的内容叫法也不一样,为了一个逻辑性。

    第一句不管,太底层了,没兴趣。回想当初学的时候,自己的追本溯源,有种成就感,我当时问老师,所有的源头,包括对象的设定,都是因为语言环境提供运行支撑的吧??



    第三句,看图,函数也有一堆内置属性的。函数名字叫什么?对吧!把函数归为面向对象研究嘛,就当成对象得了。因为函数声明你也知道,那函数是怎么造出来的?直说结果,我也不知道过程,就是对象造出来的。

    这里就要有个方法了,如何找到自己的原型?
    首先,我们根据逻辑其实知道了,arr是Array创造的,所以,规定了
    arr.proto === Array.prototype。
    所以这是判断原型的一个依据的。再说一个事实,是谁创造了Array函数呢?是Object这个函数。

    那如何判断Object是不是arr的原型?思路是先看arr的proto是不是等于Object的prototype,不等于就看父辈的proto,还不等,就继续看爷爷辈的proto,,,只要有一级相等,那就是,一般找到Object.prototype.proto ===null,这是终点了,Object.prototype指向的对象是个特殊对象,里面没有proto。这就是instanceof的判断逻辑的。
    神附加题:

    function people(){}
    var p = new people()
    p.__proto__  === people.prototype     //true,不解释
    people.__proto__ === Function.prototype    // true ,因为函数的父亲是Function
    (people.prototype).__proto__ === Object.prototype   //true ,因为Object创造了所有基本对象,
    //左边的意思就是people函数的prototype指向的那个对象的父亲的prototype。
    
    Function.prototype === Function._proto_     //true  ,Function的prototype指向了它生成的对象的 _proto_ ,
    //那这里,意思是Function是不是自己创建的,是的,如下图,,,
    Function.prototype === Object._proto_  //ture  ,Object的父亲是不是Function?Object也是个函数,,,
    Function.prototype._proto_ === Object.prototype  //true ,Function.prototype指向的对象的父亲的prototype。
    Function instanceof Object   //true  ,
    就是判断Function.__proto__ === Object.prototype,因为Function._proto_ ===Function.prototype,
    
    而Function.prototype._proto_ === Object._proto_ 
    
    Function._proto_ ._proto_=== Object._proto_ ,Function._proto_ 指向了对象。
    

    经验之谈:
    当new一个函数时,会创建一个对象,函数的prototype === 这个对象的proto
    一切函数由Function创建,包括它自己。
    一切函数的原型对象都是由Object这个函数创建的。fn.prototype.proto ===Object.prototype
    创造者是作为函数看待,有prototype,生成者被当成对象看待,有proto
    其实这里凭感觉都可以知道的,只不过有些别扭的是自己生产自己的,这个其实很容易理解,一个变量自己还能自增,自增了自己还是自己。

    对原型对象的操作,人为指定

    小练习:

    相关文章

      网友评论

          本文标题:JS原型的学法

          本文链接:https://www.haomeiwen.com/subject/pgocmxtx.html