JS面向对象一:函数对象关系、原型和原型链

面向对象

1、介绍函数和对象之间的关系

思考?什么是函数?函数有什么作用?

作用1:整合封装代码方法,让调用更加清晰
作用2:用来生成对象

任何对象都是由函数生成的,这样的函数是构造函数

思考? 1、构造函数和普通函数有什么区别?

没什么区别(哈哈哈)只是使用方式的区别

function foo(){
}

foo()//普通函数

new foo()//构造函数

一个函数当正常调用的时候就是普通函数,通过new函数()就是构造函数

但是为了区分构造函数,约定俗成的共识:构造函数 :首字母—>大写

思考?2、构造函数怎么生成对象的?

通过new函数()方式就可以生成对象

let obj = {}也是构造函数生成的对象吗?

是的,因为这种方法叫做包装对象和字面量,{ }其实是一种语法糖,直接简略掉了,相当于new Object()

拓展:
let num=3?不是说万物皆对象吗?3也是一个对象吗?怎么创造出来的?其他的数据类型也是对象吗?

是一个对象,实际上是new number(3)生成的,控制台直接(3).

也就是说万物皆对象:任何数据类型都是由构造函数生成的对象(除了undefined和null之外,这两个是开发js这个语言的人员规定的。

在这里插入图片描述

为什么3.不可以。因为3是一个数字3.也是一个数字。点的含义一个是小数点一个是调用函数,小数点的的优先级更高一点,所以要么加(括号),要么再加一个点(这都是拓展部分,只是更好的理解)

接下来继续

不同构造函数为什么创造的对象不一样?这是为什么?

构造函数(前辈)实例对象(后辈),俗话说龙生龙凤生凤,老鼠的儿子会打洞,也就是说构造函数是有基因的,创造生成对象会把基因带过去
在这里插入图片描述

那么这个基因在哪里呢?一般的属性会是基因吗?

function Foo(){}
Foo.a="妈妈染了黄色头发"
let obj =new Foo()//没有a属性

这个时候a属性传给了obj这个对象了吗?显然是没有的,obj对象不具有a属性
比如:一个妈妈在怀着孩子时候染了头发,那孩子出生时候头发和妈妈染的一样吗?显然不一样,因为染头发只是表面,不是妈妈的头发基因就是那个颜色(多么生动的举例子,掌声响起)

构造函数在生成对象的时候可以对对象进行一些设置
在构造函数中设置this就直接影响生成的对象

function Foo(){
this.name="张三"
let b="局部变量"
}
let obj = new Foo()

在这里插入图片描述
以上就是this的作用,而b仅仅是函数中的局部变量

思考?相同方式创造的两个对象一样吗?

不一样
在这里插入图片描述
只不过基因一样,但是是两个对象。

思考?对象不是引用关系类型吗?为什么还是创造出两个对象

在这里插入图片描述

首先你对引用类型理解错了,这种是引用类型的体现,创造了一个对象放在了内存当中,并使得a指向了这个对象(以赋值的思想理解吧),随后b=a使得b也指向了这个对象,所以a 和b指向的是同一个对象,只要任何一个对对象进行操作,都会使得两个同时改变,如图所示
在这里插入图片描述

又说多了接下来继续

2、原型和原型链

思考?上边我们总说基因,什么是对象的基因?

任何对象都有一个_proto_ 的属性 ,这个属性就是基因,管这个属性叫做 —> 隐式原型

思考问题?隐式原型(基因)有什么作用?

对象访问属性怎么访问的?
1、首先在内部里边去寻找
2、内部找不到之后去隐式原型里边找…(隐式原型里还有隐式原型,可以一直找。。。。)
(构造函数中的原型===实例对象中的隐式原型)

接着思考?这个隐式原型从哪里来的?

function Foo(){}
let obj = new Foo()

在这里插入图片描述
我们可以看到函数在一个constructor的属性中
constructor就是构造者,构造器,把他写到了隐式原型_proto_中,
在这里插入图片描述
我们可以看到直接用这个对象调用这个构造器,返回的是我们的构造函数,但是我们想研究构造函数和对象有什么关系,我们就要看看内部结构,怎么办?用console.dir()强行以对象的方式( 函数肯定是对象)进行打开这个函数我们来看一看哈。
在这里插入图片描述
里边有prototype
任何函数都有一条属性prototype,他是这个函数在实例对象的时候 传递的基因,prototype就是原型

所以重点来了划重点:

构造函数.prototype===实例对象.proto

什么是原型?什么是隐式原型?那这两个和prototype以及_proto_又是什么关系呢?

prototype是原型,而_proto_是一个隐形原型指向了构造函数中的prtotype,所以这两个一摸一样

思考?直接给构造函数中的prototype添加内容,会发生什么?

function Foo(){}
Foo.prototype.say ="hello"
let obj = new Foo()

在这里插入图片描述

我们看到了,直接给原型添加属性,那么我们创造出来的实例对象也会有这个属性。刚刚的例子我们原型添加放在了创建实例之前,那么我们调换顺序呢?
看一下

function Foo(){}
let obj = new Foo()
Foo.prototype.say ="hello"
console.log(obj.say)

在这里插入图片描述
还是能打印出来?为什么呢,我们后边才给原型添加的东西呀,不应该呀
我们看看哈
在这里插入图片描述
利用构造函数Foo创造了一个实例对象obj,之后执行代码内容为:给构造函数原型添加一个say属性,此时打印obj中的say,从对象身上找,找不到,找到隐形原型指向了原型,原型中有say所以找到打印出来

根据刚刚内容写一个小题目,一个空数组,一个Foo函数,现在在数组里添加100个Foo对象,我们想在每个对象中添加一个属性state=“交流过了”

   let arr = []
   let Foo = function () {
     
   } 
   for(let i;i<100;i++){
     arr.push(new Foo())
   }

现在怎么办?两种方法

第一种逐个遍历,添加

   let arr = []
   let Foo = function () {
  
   } 
   for(let i;i<100;i++){
     arr.push(new Foo())
   }
   arr.forEach(obj=>{
     obj.state="交流过了"
   })

这种方法可以实现没有问题,那如果是3000000个呢,我们一个一个遍历要浪费多少性能?太多了

第二种直接在原型上加

let arr = []
   let Foo = function () {
     
   } 
   for(let i;i<100;i++){
     arr.push(new Foo())
   }
   Foo.prototype.state="交流过了"
    

简单粗暴

思考?为什么一个是数组而一个不是呢为什么?

 <ul class="cc">
    <li></li>
    <li></li>
    <li></li>
  </ul>
   let arr = [1,2,3]
   let arrlist=document.querySelectorAll("li")

看两个东西是由什么构造函数生成的

   console.log(arr.constructor);
   console.log(arrlist.constructor); 

在这里插入图片描述
构造者不相同,所以一个是数组一个是类数组NodeList

什么是真正意义上的数组?
由Array构造函数实例出来的才是数组

总结一小下

构造函数:普通函数相似,this执行方法new,生成对象
实例对象:由构造函数生成的对象
原型
(1)函数的原型:任何函数都有prototype属性,将原型添加到对象中。
(2)对象的隐式原型:任何对象都有_proto_属性,对象的类别的信息载体
对象访问的属性:对象身上找,隐式原型找,。。。。(隐式原型有隐式原型一直找直到找到为止)

深造一把

function Foo(){}

1、Foo.prototype存在吗? 答案:存在
因为任何函数都有prototype
2、Foo._proto_存在吗?答案:存在
因为每个函数都是一个对象,任何对象都有_proto_属性

3、Foo.prototype和Foo._proto_的区别? 这是重点内容

Foo.prototype是Foo作为构造函数中的基因传递给实例对象

Foo._proto_是Foo作为对象从她母亲中继承的属性

Foo的构造函数是什么? 函数的构造函数是什么?谁创造了这个函数?

我们直接看下他的构造器/构造者即可
在这里插入图片描述
任何函数都是由同一个构造函数(Function)生成出来的
因此我们看Function的prototype,岂不是可以看到所有函数具有的方法或者属性?
在这里插入图片描述
天啊这不就是我们所有函数的属性吗,一句卧槽真牛皮,那我们在Function中的prototype中添加东西,岂不是所有的函数都有了这个东西,卧槽真爽哈!

补充:console.dir():按照一个目录结构来展开一个对象

思考?this指向谁?

Array.prototype.foo=function(){
     console.log(this);
   }
   Array.prototype.foo()//指向了Array.prototype

   let arr=[]
   arr.foo()//指向了arr

在这里插入图片描述
根据所学就可以在原型中写自己封装的方法,比如在Array.prototype中写myReduce()方法等等

 Array.prototype.myReduce=function(cb,total){
     let result = this[0]//指向实例对象
     let length = this.length
     let start =1
     if(arguments.length>=2){
       result=total
       start =0
     }
     for(let i=start;i<length;i++){
      result =cb(this[i],this[i],this)
     }
     return result
   }

我们后期补一个封装各种数组方法的文章,也算小福利了

接下里聊个小东西

思考?Object Function window之间的关系

(1)window 是最小的,window是不是对象?

(2)windows 和Object的关系

window在控制台上打印一下,一直往下找_proto_…第五代左右会找到Objeci,什么意思,Object是他祖宗,明白了吧

(3)Function 和Object之间关系,

任何对象都是由Object创造出来的
任何函数都是由Function创建出来的

这两个是原始规则

Function又是一个函数,Function创建了自己?判断一下虽然我们下图中看到结果是true,自己创造的自己,但是其实是Object创造出来的,但是为了适应那个法则,把这个功劳给到了Function头上,所以才有了ture。

那句话怎么说来着?一个人扛下来所有
在这里插入图片描述
在这里插入图片描述
好了我们再来看一个
在这里插入图片描述

什么鬼,不是Object生了Function把功劳给了他自己,怎么又出现了Object是Function生的?有病吧?

我们刚刚说的Object负责创造对象,Function负责创造函数,Object又是一个构造函数,是一个函数,所以只能退一步,把这个东西归属到Function头上,也就是说开始定的规则不能破坏,只能是委曲求全

Object.prototype是基因的源泉
Object.prototype.proto=null,也就是说是终点,是原型链的终点(对象访问属性终点)

在这里插入图片描述
万事万物皆对象,万事万物访问属性的时候都会访问到Objcet.prototype

原型链:对象访问属性访问__proto__,如果访问不到找__proto__.proto…直到找到Object.prototype.__proto__重点

这也就是说为什么任何对象都有toString方法

在这里插入图片描述
他们终究找到的toString方法是Object.protot中的toString方法

但是又有问题了why?

在这里插入图片描述
说明数字的toString有自己的构造方法
在这里插入图片描述

不光数字有自己的toString方法,字符串也有自己的toString方法

我们根据以上就可以知道怎么鉴别所有的数据类型,有人说typeof,这个只能检测基本的数据类型

Objcet.prototype.toString是不是适合所有的数据类型?

正确,可以将任意的数据类型传进去,内部使用的是this调用的主体
所以是调用统一的toString方法Objcet.prototype.toString(null和undefined),返回的数据类型结构描述一致

 	console.log(Object.prototype.toString([12,13]));
    console.log(Object.prototype.toString(undefined));
    console.log(Object.prototype.toString({}));
    console.log(Object.prototype.toString(1));
    console.log(Object.prototype.toString("2"));
    console.log(Object.prototype.toString(null));

在这里插入图片描述
???为什么都是Object,因为我们的主体是对Object进行判断的,所以需要把这个方法加载判断的内容上,需要call()

console.log(Object.prototype.toString.call([12,13]));
    console.log(Object.prototype.toString.call(undefined));
    console.log(Object.prototype.toString.call({}));
    console.log(Object.prototype.toString.call(1));
    console.log(Object.prototype.toString.call("2"));
    console.log(Object.prototype.toString.call(null));
    console.log(Object.prototype.toString.call(document.body));
    console.log(Object.prototype.toString.call(document.querySelectorAll("li")));
    console.log(Object.prototype.toString.call(/asd/));
    console.log(Object.prototype.toString.call(true));

在这里插入图片描述

掌声响起

但是这样还是有点丑,我把前边object还有大括号去掉自己封装一个type的方法

function type(obj){
    return Object.prototype.toString.call(obj).slice(8,-1)
   }

在这里插入图片描述

扩展

Object.prototype可以用{ }替换

function type(obj){
    return ({}).toString.call(obj).slice(8,-1)
   }

对象访问toString会直接到达Object.prototype中

类数组都可以直接用prototype直接转接数组方法

NodeLst.prototype.map=Array.prototype.map
NodeLst.prototype.Reduce=Array.prototype.Reduce

又要说再见了,话说张哥下章讲一下面向对象二:对象的继承

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值