这里给大家分享我在网上总结出来的一些知识 ,希望对大家有所帮助
通过 函数名.–proto– 可以得到 Function.prototape 。通过 函数名.constructor这个属性 ,可以得到它的构造函数是Function 。比较巧的是 ,Function的constructor就是它自身 ,Function的–proto–也是它的 Function.prototape 。但是规定 ,一切函数的prototape的–proto– 指向 Object.prototape ,即函数名.prototape.–proto–等于Object.prototape
对象的创建
通过new +构造函数 ,比如var obj=new Object()
通过字面量的形式 ,比如var obj={} ,这个是JOSN对象的简写 。JOSN是由Object构造的 ,也就是说
JOSN不是函数 ,也就不拥有prototape这个属性 ,JOSN.–proto–为Object.prototape,而不是Function.prototape 。JOSN.constructor为Object 。Math对象和JOSN一样 ,也是由Object实例的 。那么 ,Math.constructor也为Object,Math.–proto–也为Object.prototape值得注意的是 ,JOSN和Math已经是实例对象了 ,不可以再new一个实例对象 。也就是不能new JOSN()和new Math()等 ,这些都是错误的操作 ,这是很多新学者会出错的地方 。
函数的创建
通过函数声明式 ,如function name(){}
通过函数表达式 ,如let name=function(){}
通过Function实例 ,如let name=new Function().凡事由Function实例的 ,都是函数
函数的静态属性和方法 ,以及函数的实例属性和方法
静态属性和方法
通过函数名.属性或者方法就是静态属性或者方法 。
上代码
function Son(){} ;
Son.age=18;
Son.sex="男";
Son.play=function(){
console.log("我会玩游戏");
}
Son.sing=function(){
console.log("我会唱歌");
}
要想得到age ,sex属性以及play(),sing()方法 ,只能通过函数名.属性或者方法使用
如
console.log(Son.age,Son.sex)//18,男
Son.play()//我会玩游戏
Son.sing()//我会唱歌
不能通过new一个对象得到
比如
var son= new Son();
console.log(Son.age,Son.sex)//undefined,undefined
son.sing()//son.sing is not a function
实例属性和方法
通过在函数内部 ,用this.属性或者this.方法的就是实例属性或方法
上代码
function Son(name,age,phone){
this.name1=name;
this.age=age;
this.phone=phone;
this.dance=function(){
console.log("我会跳舞")
}
this.getAge=function(){
return this.age;
}
}
要想得到age,sex ,phone属性以及dance(),getAge()方法 ,只能new一个实例对象,
然后通过实例对象.属性或者方法使用
如
var son1=new Children("小强","18","苹果");
console.log(son1.name1);//小强
console.log(son1.age);//18
console.log(son1.phone);//苹果
console.log(son1.getAge);//18
son1.dance();//我会跳舞
不能通过函数名.属性或者方法使用
如
console.log(Children.name1);//undefined
Children.dance();//Uncaught TypeError: Parent.say is not a function
–proto–属性的例子
–proto–属性 ,指向构造它的函数的prototape属性 。比如
function Son() {
}
var son=new Son();
console.log( son.__proto__==Son.prototape)//true
console.log( Son.__proto__==Function.prototape)//true
var obj=new Object()或者 var obj=new Object({})或者var obj={}
console.log( obj.__proto__==Object.prototape)//true
console.log(Object.__proto__==Function.prototape)//true
console.log(Array.__proto__==Function.prototape)//true
console.log(JOSN.__proto__==Object.prototape)//true
console.log(Math.__proto__==Object.prototape)//true
console.log(JOSN.__proto__==Function.prototape)//false
console.log(Math.__proto__==Function.prototape)//false
console.log(Function.__proto__==Function.prototape)//true 特殊
上述表明 ,函数的__proto__属性指向的 ,都是Function.prototape ,而实例对象指向的
是构造函数的prototape属性 。
constructor属性的例子
constructor属性 ,指向构造它的函数
function Son() {
}
var son=new Son();
console.log(son.constructor==Son)//true
console.log(Son.constructor==Function)//true
console.log(Object.constructor==Function)//true
console.log(Date.constructor==Function)//true
console.log(Math.constructor==Object)//true
console.log(JOSN.constructor==Object)//true
console.log(Function.constructor==Function)//true
上述表明 ,函数的constructor属性指向的 ,都是Function ,而实例对象指向它的构造函数
prototape属性的例子
prototape属性函数特别拥有 ,对象没有这个属性
var obj={}
console.log(obj.prototape)//undefined
function Son() {
}
var son=new Son();
console.log(obj.prototape)//undefined
console.log(son.prototape)//undefined
console.log(Date.prototape)//undefined
console.log(JOSN.prototape)//undefined
console.log(Object.prototape) //{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ,...}
console.log(Son.prototape) //{constructor: ƒ}
console.log(Function.prototape) //ƒ () { [native code] }
由此可见 ,对象没有这个属性 ,并且 ,prototape的值为对象
我们可以通过prototape.constructor,可以知道这个原型对象的构造函数是谁
console.log(Object.prototape.constructor)//ƒ Object() { [native code] }
console.log(Son.prototape.constructor)//ƒ Son() { }
console.log(Son.prototape.constructor)//ƒ Function() { [native code] }
我们可以向prototape中添加方法或者属性 ,但一般添加方法 ,代表这个链上的公共方法,个人认为 ,
这个方法如果很多对象都用到了 ,不妨放在Object.prototape中。
Object.prototape.sing=function(){
console.log("我会唱歌");
}
Object.prototape.sing()//我会唱歌
首先new 一个实例对象
var son=new Son();
然后通过实例对象.方法名就可以使用
son.sing();//我会唱歌
原型链
没有改变prototape的原型链
如图
箭头指向它们的方向 ,上述关系(–proto–,constructor,prototape)都在例子中讲解了 ,就不再赘述 ,这里关注的是原型链 。(备注:本图的JOSN和Math对象不好画在图的左边 ,因此又画了一个Object.prototape原型对象在右边 。)我们看Son的实例对象 ,为了方便 ,我写一个var son=new Son()。通过son.–proto– ,可以得到Son.prorotape 。如果继续通过链式 ,即son.–proto–.–proto–可以得到Object.prototape 。我们已经知道了 ,son.–proto–就是为Son.prorotape ,那么我们通过Son.prorotape.–proto–也可以得到Object.prototape 。也就是说,–proto–能够让我们顺藤摸瓜 ,一直向上查找 ,这就是原型链(图中已经用红色大边框包裹起来了) 。当我们son.–proto–.–proto–.–proto–(相当于Object.prototape–proto–,也相当于Son.prorotape.–proto–.–proto–)时 ,得到null ,这说明 ,原型链是有终点的 。
为什么要原型链?
当我们需要一些方法时 ,别的函数已经有了这些方法 ,那么我们不需要自己再次造轮子 ,可以通过原型链 ,查找各个父级的prototape属性的值 ,得到想要的方法 。比如:我们想要让Object的实例对象可以使用sing()方法 ,同时也想让Son的实例对象使用一个sing()方法 ,那么我们就可以通过在Object的原型对象上 ,写上这个sing()共有方法 ,不需要让Son的原型对象上,再写下这个sing()方法 。
Object.prototape.sing=function(){
console.log("我会唱歌");
}
首先new 一个实例对象
var son=new Son();
然后通过实例对象.方法名就可以使用
son.sing();//我会唱歌
所以 ,通过原型链 ,可以让我们少些一些相同的代码 。我们只需要把相同的方法放在父级上,就不必自己重写一个;除此之外 ,如果我们这个实例对象的构造函数如果没有这个方法 ,那么这个实例对象会依次向原型链查找 ,直到查找到Object.prototape为止 ,如果还没有找到 ,那就终止 ,并且报错 。如果这个构造函数有这个方法 ,那么按照构造函数的来 ,而不是父级的prototape里面的方法 。这说明 ,存在一定的优先级 ,我们需要注意 。
改变了prototape的值原型链
如图
我们已经知道了prototape的值是一个对象 ,那么我让prototape的值等于一个实例对象 ,会怎么样?
如上图所示,即Son.prototape=new Parent() 。我们试着打印一下Son.prototape的值 ,console.log(Son.prototape)//Parent {},显示是parent的实例对象 ,再试着打印console.log(Son.prototape.constructor)//function Parent(){},发现竟然是Parent,你会心想 ,这就不合理了。我应该指向Son才对啊 。这里我做出解释 ,因为我们是将Son.prototape重新赋值了 ,将原本的对象覆盖了 ,即地址发生了变化 。现在的地址和之前的地址不是同一个地址(对象的重新赋值 ,会改变地址) ,那么大家是不是瞬间清楚了。同时 ,这也说明了 ,Son.prototape.constructor继承Parent.prototape.constructor ,我相信这下子大家都清楚了 ,说明constructor也可以继承 。为了解决这种问题 ,我们必须让Son.prototape.constructor重新指向Son ,所以Son.prototape.constructor=Son,这样子 ,console.log(Son.prototape.constructor)//function Son(){}就解决问题了 。之后通过–proto–与没有改变prototape的原型链是一样的 ,只不过Son.prototape.–proto–为Parent.prototape 。那么,我们new 一个Son的实例对象 ,有
var son=new Son();
console.log(son.--proto--)//Son.prototape
console.log(son.--proto--.--proto--)//Parent.prototape
console.log(son.--proto--.--proto--.--proto--)//Object.prototape
console.log(son.--proto--.--proto--.--proto--.--proto--)//null
console.log(Son.prototape.--proto--)//Parent.prototape
console.log(Son.prototape.--proto--.--proto--)//Object.prototape
console.log(Son.prototape.--proto--.--proto--.--proto--)//null
console.log(Patent.prototape.--proto--)//Object.prototape
console.log(Patent.prototape.--proto--.--proto--)//null
console.log(Object.prototape.--proto--)//null
并不是说 ,只有一个函数的prototape的值可以更改 ,我们还可以更改很多函数的prototape值 。比如Parent的prototape的值 ,让Parent.prototape=new Grandparent又加了一个父级 ,多出来一条链 。有兴趣的 ,可以去试试 。
本文转载于:
https://juejin.cn/post/7101887288652595230
如果对您有所帮助 ,欢迎您点个关注 ,我会定时更新技术文档 ,大家一起讨论学习 ,一起进步 。
声明:本站所有文章 ,如无特殊说明或标注 ,均为本站原创发布 。任何个人或组织,在未征得本站同意时 ,禁止复制 、盗用 、采集 、发布本站内容到任何网站 、书籍等各类媒体平台 。如若本站内容侵犯了原著者的合法权益 ,可联系我们进行处理 。