1

es5版本:(过两天写es6 版本的)
由浅入深地讲,
先说使用,

function person(age, sex) {
        this.age = age;
        this.sex = sex;
        this.clothes = [];
    }
    
     person.prototype.addCloud = function(cloth) {
           this.clothes.push(cloth);
    }  // 给对象增加增添服装的方法
    
    var a = new person(18, 0); // a  = {age: 18, sex: 0, clothes: []}, a 现在就是一个18岁的小女孩了
    var b = new person(19, 1); // b  = {age: 19, sex: 1, clothes: []}, b 现在就是一个19岁的小男孩了
    
    a.addCloud('redCloth') // a = {age: 18, sex: 0, clothes: ['redCloth']},a新添 红色衣服 
以上内容是原型最简单的使用,相当于构造一个对象,person被称为构造函数,new person()被称为实例化,a,b被称为实例对象

稍微增加点难度
  person.prototype.sayHello = function(word) {
      console.log( 'I am ' + this.age + 'years old!' + word);
  }
  
  a.sayHello('nice to meet you'); //I am 18years old!nice to meet you
这里输出了a的一个属性age;

再增加一点难度,插个题外话,讲一下this 的指向问题。
这里调用实例对象的age,在声明方法的时候,是用的this。刚刚增添衣服的时候,也是用的this,原因是因为a调用的这个方法,要是写一个
  var c = a.sayHello;
  c('nice to meet you') //I am undefinedyears!nice to meet you
  那么如果想像刚刚一样的执行效果有以下四种方法
  1.window.age = a.age;
  2.c.bind(a)('nice to meet you');
  3.c.call(a, 'nice to meet you');
  4.c.apply(a, ['nice to meet you']);
稍微增加点难度,实现一个jQuery的效果;
以前经常有人问为什么JQ不能混搭原生js写,这里也将作出解答。
 <script>
  function $(dom) {
      return new jq_(dom);   
  } // 返回一个jq_对象
  
  //先说一个最常用的click 方法
  
  function jq_(dom) { //构造函数
      if(typeof dom =='string')
          this.dom = document.querySelectorAll(dom); // 获取所有节点
      else if (typeof dom =='object') {
          this.dom = [dom];
      }
      this.length = this.dom.length;// 获取节点的长度
  }
  
  jq_.prototype.click = function(handle) {
      this.dom.forEach(function(dom) {
          dom.onclick = handle;
      })
      return this; // 作用于链式调用
  }
</script>  
  <div id = 'test'>测试</div>
  <script>
      $('#test').click(function(){console.log($(this).length)});
  </script>

上面的代码就可以实现jquery 的效果(因为我没读过jquery 源码。所以就不胡乱判断jquery 是不是也用这个原理)
说一下为什么jq 不能和 原生js 混搭用
从上面的内容不难看出,$之后返回的是一个实例化对象,而不是一个或多个节点。所以实际上我们并不是直接去操作节点,而且通知这个实例化对象,这个对象再去处理的节点。
上面click之后有一个return this , 这个是链式调用的核心。所谓的链式调用,表面上是一个个调用下去,比如
$().click(1).click(2)
实际上可以理解为 click完成后 返回到 $ ,然后再去click ,也就是

$().click(1) =>返回 $() 然后=> $().click(2);

如果再深入一点说

$()创建$对象 -> 对象$.click(1)执行1中事件并抛出执行结果---$对象 -> click(1)抛出的结果$对象再去执行click(2)的事件,并抛出执行结果---$对象

上面讲了创造一个对象,下面说一原型链

原型链这方面有两个关键词 __proto__ 以及 prototype

前者是针对于实例化对象的,后者是针对于构造函数

写成代码

    function test() {}
    
    new test().__proto__ == test.prototype; //true

这个如果说到内存层次的话,就是他两公用的一个内存,如果有很多实例化对象的话,就是他们__proto__的指针都指向这块内存地址

这里有一个知识点。

一个对象,如果自身属性里面没有,就会去 __proto__属性里面去搜索

就跟员工一样,有房子住自己房子,没房子就住集体宿舍。自己买了高配电脑就用自己的,没买就用公司的低配电脑。实例化对象.__proto__就像员工瞅瞅公司有啥公用物品,构造函数.prototype就是公司给员工购买啥公用物品, 所以实例化对象.__proto__ 也就和 构造函数.prototype指向一块了

所以 即便test 已经实例化了。如果 test.prototype增加一个属性, 实例化的test依旧会获得这个属性。

function test() {}
 var x = new test();
 console.log(x.age) // undefined
 test.prototype.age = 5;
 console.log(x.age) // 5

如果代码写成这样

function test() {}
 var x = new test();
 console.log(x.age) // undefined
 test.prototype.age = 5;
 x.age = 10;
 console.log(x.age) // 10

所以 x.age 实际上搜索的是 x.__proto__.age

话都说到这份上了,我就随便聊下继承吧。

最简单的继承,合二为一。

    function test() {}
    test.prototype.age = 5;
    function newTest() {}
    newTest.prototype = new test();
    new newTest().age//5

讲下这个继承的原理。 刚刚说过了
一个对象,如果自身属性里面没有,就会去 __proto__属性里面去搜索
那么new NewTest()的__proto__也就是 new test();
new Newtest()里面没有的,就去__proto__里面(new test())里面找
new test()没有的就去new test()的 __proto__里面找,然后就找到了test的prototype
当然这个继承他是没有灵魂的,因为test变化了newTest 也会变。

call继承,一次性产品
    function test() {
        this.sex = 0;
    }
    function newTest() {
        this.sex = 1;
        test.call(this);
    }
    new newTest().sex//0

call 我以前对他有过很多的理解,后来终于想到了一个最简单的理解,就是洗脑,而且是强制性的,有的替换,没有的,送你;
call的常用方法 func.call(小a, func参数);
func 大热天跑到了小a家里,问小a 有没有扇子,借过来扇了扇之后,对小a说他的扇子太破,他这边有个新的,强制塞给了小a,并收取了小a两块钱
写成代码

 function func(money) {
     console.log(this.fan);
     this.fan= 'new_fan';
     this.money -= money;
     console.log(this.fan);
 }
 
 var obj_a  = {
     money: 10,
     fan: 'pretty_fan'
 }
 
 func.call(obj_a, 2);
 console.log(obj_a);
     //pretty_fan
     //new_fan
     //{money: 8, fan: "new_fan"}

继续聊继承,call 这个继承也是不怎么有灵魂的继承,说白了就是把别人的东西都替换成自己的东西,别人没有的,给别人,弄不好还被套路一波。但是继承不到prototype里面的。

有一个继承用Object.create()去继承。。。
这个看上去比上面的高档不少,实际上也没什么用;

互不干扰式继承

    这个就显得比较有灵魂,因为单纯继承,而且还不会被干扰
    c = JSON.parse(JSON.stringify(new test()));
    因为会拷贝不到里面不可遍历的属性
    所以再合并一下之前的__proto__就行了。
    newTest.prototype = Object.assign(new test().__proto__, c);

至于其他各种花里胡哨的继承,网上一查一堆,我就不多介绍了,反正也就是花架子而已


lackdata
349 声望3 粉丝