JS中的对象和函数汇总
对象数据类型的值
{} 普通对象
[] 数组
/^$/正则
Math 数学函数
实例是对象数据类型(除了基本类型字面量创建的值)
函数的prototype属性
实例的__proto__属性(指向实例所属类的prototype)
函数也是对象数据类型的
函数数据类型值
普通函数
所有的类(包含内置和自定义的类)
typeof Object //“function” Object是内置类
typeof String //“function” String是内置类
function Fn(){}
typeof Fn //“function” Fn是类
复制代码
原型prototype(对象数据类型)
- 1、所有的
函数(类)
都`天生自带一个属性:prototype(原型)
这个属性值是
对象数据类型
的值,在当前prototype对象中,存储了类需要给其实例使用的公有的属性和方法
(Function基类的prototype是个函数但是没有prototype)
- 2、prototype是个对象数据,浏览器会默认为其开辟一个堆内存,在这个堆内存中
天生自带一个属性:constructor(构造函数)
,这个属性存储的值就是当前函数本身
- 3、
每一个对象
(包含每一个类的实例、function(因为function也是对象类型数据))都天生自带一个属性:__proto__
,属性值是当前实例所属类的原型(prototype)
,如果不能确定它是谁的实例,都是Object的实例
Function.prototype是个函数但是没有prototype
函数数据类型的__proto__都指向 Function.prototype
实例的__proto__就是所属类的prototype;只要有prototype就有constructor
原型作用:实例所使用的公有属性方法都在类的原型上
所有的对象都是Object这个类的实例
对象是它原型链上所有类的实例(不只是父级的实例,也是爷爷的实例)
- (1)Object是对象数据类型的基类在它的原型(prototype)上的__proto__属性为null,即
Object.prototype.__proto__=null
,如果要指向也是指向他自己本身 - (2)但是
Object.__proto__
指向Function.prototype(内置类都是函数,函数的__proto__都指向内置类Function.prototype)
prototype叫原型;原型是对象数据类型的
_proto_:原型链
在使用实例的方法或者属性时
如果私有属性中没有再去公有属性中找
//结合使用`构造函数模式`和`原型模式`
function Fn(name,age){
this.name=name;
this.age=age;
this.say=function(){
console.log('my name is'+this.name+'! I am'+this.age+'years old')
};
}
Fn.prototype.say=function(){
console.log('hello world')
}
Fn.prototype.eat=function(){
console.log('i like food')
}
var f1=new Fn('aaa',18);
var f2=new Fn('bbb',28);
var f=new Fn;
console.log(f.say) //显示私有属性;如果私有属性中没有再去公有属性中找
f1.say===f2.say //false 都是私有分属两个实例
f1.eat===f2.eat //true 都是公有
f1.__proto__.say===f2.say //false f1.__proto__跳过私有直接找公有 私有
f2.eat===Fn.prototype.eat //true 都是公有
f1.constructor //Fn f1是Fn的实例是一个对象数据,通过原型链先找私有,私有没有找公有(Fn.prototype)所以能找到constructor,其实找得的就是Fn.prototype.constructor
Fn.prototype.constructor //Fn
Fn.prototype.constructor===Fn //true
流程解析:
//变量提升: 开辟一个Fn的堆内存空间 存储代码字符串
Fn是函数,所以自带属性prototype(prototype 对象数据类型)
Fn.prototype这个对象浏览器会默认为其开辟一个堆内存
Fn.prototype天生自带一个属性:constructor就是Fn本身
每一个对象都天生自带一个属性:\__proto__,属性值是当前对象所属类的原型(prototype)
this.name=name; this.age=age; this.say=function 都是给执行主体(this)设置私有属性
Fn.prototype.say 手动加了一个属性say(say是function) 公共属性
Fn.prototype.eat 手动加了一个属性eat(eat是function) 公共属性
var f1=new Fn('aaa',18);
f1是 Fn类的一个实例(对象数据类型)this 指向实例f1
f1.name:aaa f1.age:19 f1.say:function (都是私有属性)
f1.__proto__ 每一个实例天生自带一个属性:__proto__,属性值是当前对象所属类的原型(prototype):Fn.prototype
var f2=new Fn('bbb',28);
f2是 Fn类的一个实例(对象数据类型)this 指向实例f2
f2.name:bbb f2.age:28 f2.say:function (都是私有属性)
f2.__proto__ 每一个实例天生自带一个属性:__proto__,属性值是当前对象所属类的原型(prototype):Fn.prototype
f1是对象数据类型 也是Object的实例
复制代码
构造函数、原型、实例的关系
每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针(constructor),而实例都包含一个指向原型对象的内部指针(__proto__)
原型链:是基于__proto__向上查找的机制。
当我们操作实例的某个属性或者方法的时候,先看自己私有属性或者方法有没有,
1、找到了,结束查找,使用自己私有的即可
2、没有找到,基于__proto__找所属类的prototype,如果找到就用这个共有的,如果没有找到,基于
原型上的__proto__(prototype.__proto__)
一直向上找到Object基类的原型为止,如果在没有,操作的属性或者方法不存在
任何数据通过__proto__都能最终找到Object.prototype
所有函数的默认原型都是Object的实例,因此默认原型都会包含一个内部指针(
__proto__
)指向Object.prototype,这就是所有自定义类都汇集成toString,valueOf的根本原因
只要是一个函数
(不管是什么类,什么类的子类),永远都是内置Function
这个类的实例,也就是任何一个函数的__proto__都指向Function.prototype,包括Function自己,即Function.prototype===Function.__proto__ 为true
任何值都能用对象原型上(Object.prototype)提供的方法
只要是函数就能用call、apply、bind
Function.prototype 是一个匿名函数anonymous,但是他没有prototype,它的运行机制和普通原型对象一模一样,其__proto__指向Object.prototype(Function.prototype.__proto__ === Object.prototype)
- Object.hasOwnProperty:通过它的
__proto__
找到Object.prototype.hasOwnProperty, - 因为Object是类(函数)
Object.__proto__
找到Function.prototype
,再通过Function.prototype.__proto__
找到Object.prototype上的hasOwnProperty
function Fn(){
this.n=100;
}
Fn.prototype.getN=function(){
console.log(this.n)
}
Fn.AA=200;
var f=new Fn();
复制代码
Object是对象数据类型的基类,它的原型上的__proto__属性为null,即
Object.prototype.__proto__=null
,如果要指向也是指向他自己本身;但是Object.__proto__指向Function.prototype
(类都是函数,函数的__proto__都指向内置类Function.prototype)
对象的__proto__ 指向所属类的prototype;函数的__proto__指向内置类Function的prototype
var f=new Fn(); f是Fn的实例 是new Fn()返回的那一个对象,而不是function;所以它的__proto__没指向Function.prototype
内置类原型链引发的一些底层问题思考
arr.push():ary **
首先通过原型链的查找机制,找到Array原型上的push方法,然后让push方法执行
**向数组末尾追加所以arr._proto_.push()也能找到push方法(因为arr.__proto_就是Array的原型)但是push中的this就变了,this变为了arr._proto;是向arr._proto_(即Array.prototype)中添加
Array.prototype.push() 也能找到push方法是向Array.prototype中添加
某一个实例或者某一个对象之所以能操作某些属性和方法,是因为在他的原型链上能找到这些属性和方法,如果找不到则不能进行操作
首先通过原型链的查找机制,找到这个方法,然后才能让这个方法执行
数组和类数组的区别:
数组的__proto__指向Array,所以能用Array的方法(push等)
类数组的__proto__指向Object,所以不能用Array的方法(push等)
数组属于Array的一个类,类数组直属于Object的一个类不能用Array的方法
私有属性和公有属性是一个相对论,我们需要看相对于哪个对象而言:
1、相对于实例来说push是共有的
2、相对于Array.prototype来说push是私有的
凡是通过__proto__找到的属性都是公有的,反之都是私有的
var arr=[];
arr.hasOwnProperty('push') false
'push' in arr true
Array.prototype.hasOwnProperty('push') true
复制代码
arguments.__proto__=Array.prototype 这样arguments就能用数组的方法了(IE浏览器屏蔽了修改__proto__)
原型链中的this
关于原型链中提供的私有或公有方法存在的this指向问题:
- 1、看点前面是谁,this就是谁
- 2、把需要执行方法中的this进行替换
- 3、替换完以后,如果想要知道结果,只需要按照原型链的查找机制去查找就行了
function Fn(name,age){
this.name=name;
this.age=age;
this.say=function(){
console.log(this)
console.log('my name is'+this.name+'! I am'+this.age+'years old')
};
}
Fn.prototype.say=function(){
console.log(this)
console.log('hello world')
}
Fn.prototype.eat=function(){
console.log(this)
console.log('i like food')
}
var f1=new Fn('aaa',18);
var f2=new Fn('bbb',28);
f1.say() //this:f1 找的是私有的属性和方法
f1.__proto__.say() //this:f1.__proto__找的是共有的属性和方法(Fn.prototype的) 'hello world'
Fn.prototype.say() //this:Fn.prototype 找的是共有的属性和方法 'hello world'
复制代码
在原型上批量扩展属性和方法
1、设置别名法
2、重新构造原型,让原型指向自己开辟的堆内存,
为了保证constructor指向原来的构造函数,自定义对象必须加上constructor属性
;存在一个问题:自己开辟的堆内存中没有constructor这个属性,所以实例在调取constructor的时候找到的是Object,这样不好的,此时我们应该重新设置一下constructor,保证机制的完整性;
重新做原型指向后,之前在浏览器默认开辟的堆内存中存储的属性和方法都没用了,只有在新内存中存储的才是有用的,所以在原来原型链有东西时,慎重使用;并且
内置类不允许这么做(重构原型)
function Fn(name,age){
this.name=name;
this.age=age;
}
//加1个方法
Fn.prototype.aa=function(){
}
//批量增加
//1、设置别名法
var pro=Fn.prototype; //指向同一个堆内存
pro.aa=function(){}
//2、重新构造原型
Fn.prototype={
//重新设置一下constructor,保证机制的完整性
constructor:Fn,
aa:function(){},
bb:function(){}
}
var f=new Fn('aaa',18);
复制代码
//jQuery源码
~function(){
var jQuery =function(selector,context){
return new jQuery.fn.init(selector,context);
}
jQuery.fn=jQuery.prototype={
constructor:jQuery,
init:function(selector,context){
}
};
window.$=window.jQuery=jQuery;
}()
复制代码
基于内置类原型扩展方法
1、我们新增加的方法最好设置一个前缀:防止我们新增加的方法和内置的方法冲突,把内置方法替换掉了
2、在扩展的方法中this就是当前要处理的数据 所以
不能传参
(arr.myDistinct())arr.push()执行完以后返回的是push完以后数组的长度,是个数字,不能再使用数组的方法
扩展原型方法间的区别
1、Fn.prototype.xxx是向 现有公有方法中添加,
实例.__proto__能找到Fn.prototype;实例.constructor就是Fn
2、Fn.prototype={}是 改变公有方法指向,
如果不用constructor=Fn实例.__proto__不能找到Fn.prototype;实例.constructor就是Object
//1
function Fn(name,age){
this.name=name;
this.age=age;
}
Fn.prototype.say=function(){
console.log(this)
console.log('hello world')
}
var f1=new Fn('aaa',18);
console.log(f1.constructor) //Fn
//2
function Fn(name,age){
this.name=name;
this.age=age;
}
Fn.prototype={
say:function(){
console.log(this)
console.log('hello world')
}
}
var f1=new Fn('aaa',18);
console.log(f1.constructor) //Object内置类
//3
function Fn(name,age){
this.name=name;
this.age=age;
}
Fn.prototype={
constructor:Fn,
say:function(){
console.log(this)
console.log('hello world')
}
}
var f1=new Fn('aaa',18);
console.log(f1.constructor) //Fn
复制代码
链式写法原理:
执行完成一个方法后,返回的结果依然是当前类的一个实例,这样就可以继续调用当前类的其他方法操作(所以原型扩展方法要写上return this)
Number(undefined) = NaN 假
//数组去重基本写法
function distinct(arr){
var obj={}
for(var i=0;i<arr.length;i++){
var item=arr[i];
if(typeof obj[item] !=== 'undefined'){
arr[i] == arr[arr.length-1];
arr.length--;
i--;
}
obj[item] = item;
}
obj = null;
return arr;
}
var arr=[1,2,1,3,4,5,6,4,5,64,2,5,1,2,1]
console.log(distinct(arr))
复制代码
//在原型上扩展去重方法
Array.prototype.myDistinct=function myDistinct(){
//this就是arr(当前要处理的那个数组) 所以不能传参
var obj={}
for(var i=0;i<this.length;i++){
var item=this[i];
if(typeof obj[item] !=== 'undefined'){
this[i] == this[this.length-1];
this.length--;
i--;
}
obj[item] = item;
}
obj = null;
return this; //=>实现链式写法,返回去重后的数组,这样执行完成这个方法后,我们还可以继续调用数组中的其他方法(不考虑链式调用时可以不写)
}
var arr=[1,2,1,3,4,5,6,4,5,64,2,5,1,2,1]
arr.myDistinct()
复制代码
//链式写法:执行完成一个方法紧跟着就调取下一个方法
//链式写法原理:执行完成一个方法后,返回的结果依然是当前类的一个实例,这样就可以继续调用当前类的其他方法操作
arr.sort(function(a,b){
retrun a -b;
})
arr.push(100)
||
||
\/
arr.sort(function(a,b){
retrun a -b;
}).push(100)
//arr.push()执行完以后返回的是push完以后数组的长度,是个数字,不能再使用数组的方法
复制代码
示例
//=> 实现(3).plus(2).minus(1) =4
//简单
Number.prototype.plus=function plus(){
return this+arguments[0];
}
Number.prototype.minus=function minus(){
return this-arguments[0];
}
//严谨 考虑到不传参的情况下
Number.prototype.plus=function plus(){
var val=Number(arguments[0]) || 0;
return this+val;
}
Number.prototype.minus=function minus(){
var val=Number(arguments[0]) || 0;
return this-val;
}
复制代码