深度剖析js原型及面向对象

柯里蝉翼
• 阅读 961

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);

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

点赞
收藏
评论区
推荐文章
待兔 待兔
1年前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Karen110 Karen110
4年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
Wesley13 Wesley13
4年前
javabean转jsonObject
工程场景:需要将一个javaBean转换成jsonObject对象。假设UserusernewUser();有三个属性,name、age、sex,我们对其设值user.setName("张三");user.setAget("32");user.setSex(null);现需要转换,我采用的方法是:JS
菜园前端 菜园前端
2年前
什么是 ECMAScript?
原文链接:什么是ECMAScript?ECMAScript是JavaScript的一个标准,也是代表JavaScript的版本。2015年6月发布的版本为ES2015,ES2015以及之后的版本都统称"ES6"。ES2015之前的版本则是"ES5"。版本历
Wesley13 Wesley13
4年前
JavaWeb 之 JSON
一、概述  1、概念JSON:JavaScriptObjectNotation JavaScript对象表示法  2、基本格式varp{"name":"张三","age":23,"sex":"男"};  3、用途和优点(1)json现在多用于存储
Stella981 Stella981
4年前
ES6 系列之 Babel 是如何编译 Class 的(上)
_摘要:_ 前言在了解Babel是如何编译class前,我们先看看ES6的class和ES5的构造函数是如何对应的。毕竟,ES6的class可以看作一个语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。constructorES6中:\
Stella981 Stella981
4年前
ES6中的super
对象方法中的super原型对于javascript来说非常重要,甚至可以说是javascript中最为重要的知识点。然而,很多人一看到原型就懵逼。ES5我们可以使用Object.getPrototypeOf()来返回原型对象,使用Object.setPrototypeOf()来设置原型对象。看下面的例子:letpe
Stella981 Stella981
4年前
JavaScript面向对象编程的15种设计模式
在程序设计中有很多实用的设计模式,而其中大部分语言的实现都是基于“类”。在JavaScript中并没有类这种概念,面向对象编程不是基于类,而是基于原型去面向对象编程,JS中的函数属于一等对象,而基于JS中闭包与弱类型等特性,在实现一些设计模式的方式上与众不同。ps:本文之讲述面向对象编程的设计模式策略,JavaScript原型的基础请参考阮一峰面向
Wesley13 Wesley13
4年前
ES6面向对象
ES6面向对象js中的面向对象functionUser(name,age){this.namename;//定义属性this.ageage;}//通过原型添加方法User.prototype.showNamefuncti
Stella981 Stella981
4年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Stella981 Stella981
4年前
ES6中数组方法( every 和 some )
判断对象数组中每个人是否成年,大于17成年,否则未成年vararr{name:'jerry',sex:'man',age:14},{name:'jack',sex:'woman',age:19},{name:'bill',sex:'ma