💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[toc] ## previously JS中的对象和函数汇总 >对象数据类型的值 >- `{}`普通对象 > - `[]` 数组 > - `/^$/` 正则 > - `Math` 数学函数 > - 一般类的实例都是对象数据类型 > - 函数的`prototype`属性 > > - 实例的`__proto__`属性 > - ... > > 函数数据类型值 > - 普通函数 > - 所有的类(内置类和自定义类) > - ... ``` typeof Object === 'function' typeof String === 'function' typeof Fn === 'function' ``` ## 原型(xx.prototype) >1、 所有的函数都天生自带一个属性:prototype(原型),它是一个对象数据类型的值,在这个对象中存储了为其类的实例提供的公共属性和方法。 > >2、 prototype这个对象,浏览器会默认为其开辟一个堆内存,在这个堆内存中天生自带一个属性:constructor(构造函数),这个属性存储的值就是当前函数本身。 > >3、 每一个类的实例(每一个对象)都天生自带一个属性:`__proto__`,属性值是当前对象所属类的原型(prototype)。 > >4、 每一个类的原型的`__proto__`都指向`Object`类的原型 ``` function Fn(name,age){ } ``` ``` 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 love food!'); } var f1 = new Fn('ahhh',11); var f1 = new Fn('ahhh2',70); ``` ![](https://box.kancloud.cn/e42c605c3ee662ce123251fc8b9a7d62_1535x581.png) ## 原型链(`__proto__`) 也是一种设计模式,原型链模式 若一个对象下的属性或方法在自己身上找不到,会顺着`__proto__`网上找,`__proto__`指向的是另外一个对象,这个对象中有的方法和属性,都能传递给下层的对象 ![](https://box.kancloud.cn/4a53ffe0b3529f4343aca6ed6b989d2a_1771x566.png) ### this问题 > 关于原型链中提供的私有(公有)方法中的THIS指向问题 > 1、 看点前面是谁,THIS就是谁 > f1.say():this->f1 > f1.__proto__.say():this->f1.__proto__ > Fn.prototype.say():this->Fn.prototype > ... >2、 把需要执行方法中的THIS进行替换 >3、 替换完成后,如果想要知道结果,值需按照原型链的查找机制去查找即可 ### 向原型上批量增加属性和方法 ``` function Fn(name,age){ this.name = name; this.age = age; } Fn.prototype.aa = function(){ }; Fn.prototype.bb = function(){ }; ... var f = new Fn('xxx',28); ``` #### 设置别名 ``` //设置别名 var pro = Fn.prototype; //=>指向同一个堆内存 ``` #### 重新构造原型(内置类不支持,内置类想添加方法和属性只能一个个加) ``` Fn.prototype = { //=>让原型指向自己开辟的堆内存有一个问题,自己开辟的堆内存中没有constructor这个属性,所以实例在调取constructor的时候找到的是Object,这样不好,此时我们应该重新设置以下constructor,保证机制的完整性 constructor:Fn ,aa:function(){ } ,bb:function(){ } }; ``` #### 基于内置类的原型扩展方法 >1、 我们新增加的方法最好设置一个前缀,防止我们新增加的方法和内置的方法冲突,把内置的方法替换掉 >2、 返回的结果依然是当前类的实例,这样就可以继续调取当且类的其它方法操作了。 ``` //其中obj只是一个key的清单 function distinct(ary){ var obj = {}; for(var i=0;i<ary.length;++i){ var item = ary[i]; if(typeof obj[item]!=='undefined'){ ary[i] = ary[ary.length-1]; ary.length--; i--; continue; } obj[item] = item; } } //内置方法的方法是一个具名函数 Array.prototype.myDistinct = function myDistinct(){ //=>this:ary当期那要处理的数组 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--; continue; } obj[item] = item; } obj = null; return this; } ``` ![](https://box.kancloud.cn/5c822c66c8a9dc3981b93b3b780279b1_212x85.png) ## 习题 实现(3).plus(2).minus(1) =>4 >[warning] **注意:** `()`能将一个数值包装成一个真正的数字类型的实例。(可以使用实例中的方法),其次数值类的this就是数字本身。 ``` Number.prototype.plus = function plus(){ var value = Number(arguments[0])||0; return this+arguments[0]; } Number.prototype.minus = function minus(){ var value = Number(arguments[0])||0; return this-arguments[0]; } console.log((3).plus(2).minus(1)) ``` ## 函数 ### previously Function -> 函数类 类都天生自带一个属性`prototype` 它的原型上有`call`、`apply`、`bind`等方法 它的原型的`__proto__`也指向`Object.prototype`。 所有函数(包括类)都是`Function`的实例 **所有函数都是`Function`这个类的实例,So所有函数都能作为一个实例存在,也就是一个对象。** ![](https://box.kancloud.cn/b6c62e344d388ab313d18894c3f526d3_1683x773.png) `Object`类的`__proto__`指向`Function`,而`Function`的`prototype`指向的是`Object.prototype`。 并且`Function`的`__proto__`指向自己的prototype ![](https://box.kancloud.cn/d9cb9ac84c9c68097f0999a779aad9bc_313x65.png) `Function`的原型是**函数数据类型**的值,但相关操作和之前的一模一样。->anonymous/Empty ### 三种角色 >函数本也会有一些自己的属性: >`legnth`:0 形参的个数 >`name`:“Fn”函数名 >`prototype`: 类的原型,在原型上定义的方法都是当前Fn这个类实例的公有方法 >`__proto__`:把函数当做一个普通的对象,指向Function这个类的原型 >函数在整个JS中是最复杂也是最重要ode只是 >1、 一个函数存在多面性 >->它本身就是一个普通的函数,执行的时候形成私有的作用域(闭包),形参赋值,预解释,代码执行,执行完成后栈内存销毁/不销毁 >->“类”:它有自己的实例,也有一个叫做prototype属性是自己的原型,它的实例都指向自己的原型 >->“普通对象”:和var obj={}中的obj一样,也是一个对象,它作为对象可以有一些自己私有的属性,也可以通过`__proto__`找到Function.prototype 三种角色之间没有必然联系 ``` function Fn(){ var num = 500; this.x = 100; } Fn.prototype.geX = function(){ console.log(this.x); } Fn.aaa = 1000; //类 var f= new Fn; f.num -> undefined f.aaa -> undefined; //普通函数 var res = Fn(); //->Fn中的this是window res = undefined; //普通对象 Fn.aaa = 1000; ``` ### call ``` //改变this关键字 //Function.prototype.call = function(){} var obj = {name:'ahhh'} function fn(){ console.log(this); } fn(); // obj.fn(); fn.call(obj) //->首先我们让原型上的call方法执行,在执行call方法的时候,我们让fn方法中的this变为第一个参数的值,即obj;然后再把fn这个函数执行 Function.prototype.myCall = function(context){ //->myCall方法中的this就是当前我要操作和改变其this关键字的那个函数名 //->1、让fn中的this关键字变为context的值 //->让this这个函数中的"this关键字"变为context var that = eval(this.toString().replace("this",context)); //->2、让fn方法再执行 that(); }; fn.myCall(obj); ``` ``` function fn1(){console.log(1)}; function fn2(){console.log(2)}; fn1.call(fn2); ``` ``` fn1.call.call(fn2); ``` 首先fn1通过原型链找到callFunction.prototype上的call方法,然后再让call方法通过原型再找到Function原型上的call(因为call本身的值也是一个函数,所以同样可以找到Function.prototype),在第二次再找到call的时候让方法执行,方法中的this是fn1.call,首先让这个方法中的this变为fn2,然后再让fn1.call执行 ``` var obj = {name:'ahhh'} function fn(num1,num2){ console.log(num1+num2); console.log(this); } fn.call(100,200); fn.call(obj,100,200); fn.call(); //this->window fn.call(null);//this->window fn.call(undefined);//this->window ``` 严格模式下this指向 ``` "use strict";//=>告诉当前浏览器接下来的JS代码将按照严格模式进行编写 fn.call(); //this->undefined fn.call(null);//this->null fn.call(undefined);//this->undefined ``` ### apply apply和方法的作用是一模一样的,都是用来改变this关键字并且把方法执行;而且在严格模式下和非严格模式下对于第一个参数是null/undefined这种情况的规律也是一样的 ``` fn.call(obj,100,200); fn.apply(obj,[100,200]); ``` call在给fn传递参数的时候,是一个个传递值的,而apply不是一个个传的,而是把要给fn传递的参数值统一的放在一个数组中进行操作的->但是也相当于一个个的给fn的形参赋值 ### bind 这个方法在IE6~8不兼容 此方法和call/apply类似,都是用来改变this关键字的 ``` fn.call(obj,1,2); //->改变this和执行fn函数是都一起完成了 //->预处理:事先把fn的this改变为我们想要的结果,并且把对应的参数值也准备好,以后要用到了,直接执行即可 var tempFn = fn.bind(obj,1,2); //->只是改变了fn中的this为obj,并且给fn传递了两个参数1、2,但此时并没有把fn这个函数执行,执行bind会有一个返回值,这个返回值tempFn就是我们把fn的this改变后的那个结果 tempFn(); ```