JS原型的学法

从初学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
其实这里凭感觉都可以知道的,只不过有些别扭的是自己生产自己的,这个其实很容易理解,一个变量自己还能自增,自增了自己还是自己。

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

小练习:
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容