个人博客地址:https://blog.3xnb.com
JavaScript 进阶篇
面向对象
概述
面向对象编程的全称为Object Oriented Programming,简称为OOP。 面向对象编程是用抽象方式创建基于现实世界模型的一种编程模式。 面向对象编程可以看作是使用一系列对象相互协作的软件设计。 面向对象程序设计的目的是在编程中促进更好的灵活性和可维护性。 凭借其对模块化的重视,面向对象的代码开发更简单,更容易理解。 "面向对象编程的三个主要特征是: (1) 封装; (2)继承; (3)多态。" 所有的程序是由一定的属性和行为对象组成的,不同的对象的访问通过函数调用来完成,对象间所有的交流都是通过方法调用,通过对封装对象数据,提高复用率。 ----------------------------------------------------------------------------------------- " JS是一种基于原型的面向对象语言,而不是基于类的。"
定义函数
1、函数关键字(最常用) function fun(){ console.log("我是关键字定义的函数") } ------------------------------------------------------------------------------ 2、字面量表达式(函数体固定,无法动态执行,若有同名变量将会被覆盖) var fun=function(){ console.log("我是字面量定义的函数") } --------------------------------------------------------------------------------- 3、Function类型定义(效率低,由Function构造的函数参数可变,最后一个参数为函数体,其他参数为形参) var person=new Function( "name", "age", "console.log(name,age)" ) person("jine",22) /* 结果:jine 22 */
JS没有函数重载
在JavaScript中,如果定义多个同名函数,只有最后一个函数定义有效 例: function add(a,b){ return a+b; } function add(a,b,c){ return a+b+c; } add(1,2) add(1,2,3) /* 结果为: NaN 6 由结果可看出来第一个为NaN,是因为用的是三个参数的函数。 */
arguments 可模拟重载
可用arguments模拟函数重载 存在与函数体中的特殊对象: 1、原本是Function类型的arguments属性 2、用于接收函数的实参 3、结果类似数组的对象 例: function fun(){ console.log(arguments) /*结果为:{"0":1,"1":2,"2":3}*/ console.log(arguments[0]) /*结果为:1*/ } fun(1,2,3) ---------------------------------------- length属性:获取函数实参的个数 模拟函数重载,例如: function add(){ var len=arguments.length; if(len==2){ return arguments[0]+arguments[1] } else if(len==3){ return arguments[0]+arguments[1]+arguments[2] }else{ return arguments } } add(1) add(1,2) add(1,2,3) /*arguments.length用来接收实参的值,所以可以来模拟函数重载*/ arguments.callee() /*用于引用该函数的函数体内当前正在执行的函数。 递归的调用,此时运行的自身的函数 */
函数递归
函数体中不断调用自身。 var num=0 /*设置递归出口*/ function fun(){ console.log("开始递归了~") num++ if(num<6){ fun() }else{ return; } } fun() ---------------------------------------- var num=0 /*设置递归出口*/ function fun(){ console.log("开始递归了~") num++ if(num<6){ /*f() 此时的fun()的内存已经被释放,所以不能调用,会报错 */ arguments.callee() /*递归的调用,此时运行的自身的函数*/ }else{ return; } } f=fun /*将fun函数赋值给f*/ fun=null /*释放fun内存*/ f() /*调用f()*/
特殊函数
匿名函数
JavaScrip可以将函数作为数据使用。 作为函数本体,它像普通的数据一样,不一定要有名字。 默认名字的函数被称之为匿名函数。 匿名函数-表示没有名称的函数 ★问题:JavaScript语法并不支持匿名函数 *应用: 1、回调函数-将一个函数作为另一个函数的参数使用,作为参数的函数 2、自调函数-函数调用自身(定义即调用的函数) 如下示例: function (a){return a;} 匿名函数的两种用法: ●可以将匿名函数作为参数传递给其他函数。 这样,接收方函数就能利用所传递的函数来完成某些事 情。 ●可以定义某个匿名函数来执行某些一次性任务。
回调函数
当一个函数作为参数传递给另一个函数时,作为参数的函数被称之为回调函数。 匿名回调函数优势: 1、不占用全局命名空间(内存) 2、将私有的数据内容开放给指定位置使用 3、虽然可以使用私有数据,但不清楚来源(封装) 回调缺点: 相当于函数不断嵌套,外层函数的参数是另外第一个函数。这样会使性能下降 例如: function fun(arg){ var name="jine" arg(name) } fun(function(x){console.log(x)}) /* 执行顺序:调用fun()入口,里面将匿名函数当作fun的形参传入,去寻找定义fun()函数,在fun()函数体中调用已当作实参传入的匿名函数和传入的实参,去寻找定义的匿名函数将实参传入到形参执行。 */ ---------------------------------------------------------------------------------------- function add(a, b){ return a()+ b(); } var one = function(){return 1;} var two = function(){return 2;} console.log(add(one,two)); //3 //可以直接使用匿名函数来替代one()和two(),以作为目标函数的参数 console.log(add(function(){return 1;}, function(){return 2;});
自调函数
作用:用于执行一次性的逻辑任务 应用:作为整体逻辑代码外层结构(因为没有函数名,全局作用域访问不到) 语法结构: *第一个括号:定义函数 *第二个括号:调用函数 1、表达式方式 国内写法: (function(v){ console.log('this is ' + y); })('function'); 国外写法: (function(v){ console.log("this is "+y); }("function")) 2、其他写法(不常用,有大概20多种): !(function(v){ console.log('this is ' + y); })('function'); +(function(v){ console.log('this is ' + y); })('function'); ~(function(v){ console.log('this is ' + y); })('function');
作为值的函数
在函数体中定义另一个函数(内部的私有函数) function fun(){ return function(){ return "jine" }; } var result=fun() console.log(result) /*function fun()--*/ console.log(result()) /*jine*/
闭包
JavaScript允许函数嵌套,井且内部函数可以访问定义在外部函数中的所有变量和函数,以及外部函数能访问的所有变量和函数。 但是,外部函数却不能够访问定义在内部函数中的变量和函数。 当内部函数以某一种方式被 任何一个外部函数作用域访问时,一个闭包就产生了。 闭包就是词法表示包括不必计算的变量的函数,也就是说,该函数能使用函数外定义的变量。 闭包的作用: ●提供可共享的局部变量。 ●保护共享的局部变量。提供专门的读写变量的函数。. ●避免全局污染。 例如: function fun1(arg){ var a=100; arg(a) } function fun2(arg){ console.log(arg) } fun1(fun2)
作用域链
var a="a" function fun(){ var b="b" console.log(a) console.log(b) console.log(c) /* 只能访问到a和b */ function fn(){ var c="c" console.log(a) console.log(b) console.log(c) /* 结果可以访问到变量a、b、c */ } }
Function 类型
Function类型是. JavaScript提供的引用类型之一, 通过Function类型创建Function对象。 在JavaScript中,函数也是以对象的形式存在的。 每个函数都是一个Function对象 。 函数名,本质就是一个变量名,是某个Function对象的引用。 function fn(){ console.log('hello'); } console.log(fn instanceof Function);// true ----------------------------------------------------------------------------------------- 使用Function类型创建一个函数对象 例: var fun =new Function('a,b','console.log("this is " + a)') fun("第一个实参","第二个实参") 可用instanceof来判断Object和Function和函数之间的关系: 个人理解:Function既是一个引用类型(看作为类或者是构造函数或叫做对象模板)也是一个包装类型(看作为对象),而函数便是一个Function类型的对象。而js中所有的对象都是Object类型。 Function引用类型可以当作是一个构造函数,构造函数便是一个Functon类型的对象,所以Object也是一种Function类型对象 总结:万物皆对象,函数就是一个对象,函数名仅仅保存指向函数对象的指针,每个函数都是Function类型的实例
length属性
function fun(a,b){ console.log("函数") } console.log(fun.length) /*length属性获取的是形参的个数*/ "如若没有形参,默认便为0"
apply() 方法
Function类型的apply()方法用于调用一个函数,并且接受指定的this值,以及一个数值作为参数。 结构如下: function fun(){ xxxxxxx } fun.apply(thisArg, [argsArray]) /*因apply是Function类型中的方法,而fun又是Function实例化的对象,所以此时的fun是一个对象,且便有了对象模板Function中的方法apply*/ ●thisArg参数: 可选项,在func函数运行时使用的this值(当前调用函数的对象)。"如果不使用,可提供默认值为null或undefined" ●argsArray参数(接受指定函数的实参): 可选项,一个数组或者类数组对象,其中的数组元素将作为单独的参数传给func函数。也可以使用arguments对象作为该参数。 ●返回值:调用该函数的返回结果。 例: function fun(name,age){ console.log("姓名:"+name,"年龄:"+age) } fun("jine",22) fun.apply(null,["jine",22]) fun.apply(undefined,["jine",22]) fun.apply(fun,["jine",22]) /*以上结果都相同*/
call() 方法
Function的cal()方法用于调用一个函数,并且接收指定的this值作为参数,以及参数列表。 结构如下: func.call(thisArg,arg1,arg2, ..) ●thisArg参数:在func函数运行时使用的this值。 ●arg1, arg2,参数:指定的参数列表。 ●返回值:调用该函数的返回结果。 例: fun("jine",22) fun.call(null,"jine",22) fun.call(undefined,"jine",22) fun.call(fun,"jine",22) /*以上结果都相同*/ apply()与call()非常相似,不同之处在于提供参数的方式。 "apply()参数为数组,而call()参数为列表"
bind() 方法
Function的bind()方法用于创建一个新的函数(称为绑定函数),井且接收指定的this值作为参数,以及参数列表。 语法结构如下: fun.bind(thisArg[,arg1[,arg2[...]]]) ●thisArg参数:当绑定函数被调用时,该参数会作为原函数运行时的this指向。 ●arg1,arg2,....参数:绑定函数被创建时的实参。(新创建的函数作调用时,传递的实参无效) ●返回值:返回由指定的this值和初始化参数改造的原函数拷贝。 function fun(arg){ console.log("this is "+arg) } fun("实参") /*结果:this is 实参*/ var f=fun.bind(null,'新传入的第一个实参') f() /*结果:this is 新传入的第一个实参*/ f("新传入的第二个实参") /* 没有打印出结果,并不是传入实参无效,因绑定时有了第一个预定的参数,在进行调用传参的话便被当作第二个参传入,而上面的fun()函数的形参只有一个,那么其他的参数便接受不到。 可进行测试: function fun(arg,args){ console.log("this is "+arg,"this is "+args) } fun("实参") /*结果:this is 实参 this is undefined*/ var f=fun.bind(null,'新传入的第一个实参') f() /*结果:this is 实参 this is undefined*/ f("新传入的第二个实参") /*结果:this is 新传入的第一个实参 this is 新传入的第二个实参*/ */ "注意:bind()后的新函数传入的实参,并不会改变或影响原有函数的实参,相当于再内存中一共俩块内存,而又互不干涉。这种bind()后被称作深克隆(克隆内容,是俩个内存,相当于内存拷贝)。补充:浅克隆(共享内存)相当于内存的指针的拷贝"
Object 类型
构造函数构造对象 当以非构造函数形式被调用时,Object等同于 new Object() var a= Object() //函数调用创建对象 var b= new Object() //构造函数调用创建对象
构造函数
构造函数又称为构造器或对象模板,也称为类,是对象中的一个方法,在实例化时构造器被调用。 在JavaScript中函数就可以作为构造器使用,因此不需要特别地定义一个构造器方法。 例: function Obj(name,age){ this.name=name this.age=age this.fun=function(){ console.log("开始打印:"+name,+age) } } var person=new Obj("jine",22) person.fun()
构造对象
1、初始化器直接构造对象 var obj={ name:"jine", age:22 } obj.name obj.age -------------------------------------------------------------------------------------- 2、构造函数(对象模板)构造对象 "(效率高)"" function Obj(name,age){ this.name=name this.age=age } var xiaowang=new Obj("小王",20) var xiaoxiao=new Obj("小小",22) "俩种对比:初始化器每次创建对象都要写一次。而构造函数构造对象,只需要new,便可new出多个不一样的对象" console.log(obj.constructor) /* constructor 属性 是来自于Object类型,而js中所有的对象都是Object类型,那么每创建一个对象便带有一个constructor属性,这个属性叫做构造器,作用是:将开辟出来的对象内存地址,指向到对应的对象模板,那这样创建出来的对象便会有了对象模板中的属性和方法 */ ---------------------------------------------------------------------------------------- 3、Object() 构造对象 var obj={ name:"jine", age:22 } var Person=Object(obj) /*第一种:obj通过Object()将自有的属性和方法复制到新创建的对象Person中*/ ------------------------ var newObj=Object() newObj.name="jine" newObj.age=22 /*第二种:通过Object()使newObj成为一个空对象,然后再通过添加对象的属性和方法进行添加*/
操作对象属性
属性描述符
JavaScript提供了一个内部数据结构,用于描述对象的值,控制其行为。 例如该属性是否可写、可配置、可修改以及可枚举等。 这个内部数据结构被称为“属性描述符”。 每个属性都有自己对应的属性描述符,保存该属性的元信息。 { value:'jine', writable: false, enumerable: true, configurable: false, get: undefined, set: undefined } 对象里目前存在的属性描述符有两种主要形式:"数据描述..符和存取描述符。" ----------------------------------------------------------------------------------------- 1、数据描述符 数据描述符是一个具有值的属性,该值可能是可写的,也可能不是可写的。 数据描述符具有以下可选键值: ●value: 该属性对应的值。可以是任何有效的JavaScript值(数值,对象,函数等)。默认为undefined。 ●writable:当取当该属性的writable为true时,value才能被赋值运算符改变。默认为false。 ●configurable: 当且仅当该属性的configurable为true时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为false。 ●enumerable:当且仅当该属性的enumerable为true时,该属性才能够出现在对象的枚举属性中。默认为false。 ----------------------------------------------------------------------------------------- 2、存取描述符 存取描述符是由getter-setter函数对描述的属性。存取描述符具有以下可选键值: ●get:给属性提供getter的方法,如果没有getter则为undefined。 当访问该属性时,该方法会被执行,方法执行时没有参数传入,但是会传入this对象。 ●set: 给属性提供setter的方法,如果没有setter则为Undefined。 当属性值修改时,触发执行该方法。该访法将接受唯一参数, 即该属性新的参数值。 ●configurable: 当且仅当该属性的configurable为true时,该属性描述符才能够被改变,同时该属 性也能从对应的对象上被删除。默认为false。 ●enumerable: 当且仅当该属性的enumerable为true时,该属性才能够出现在对象的枚举属性中。默认为false。
获取属性描述符
Object.getOwnPropertyDescriptor()方法返回指定对象上一个自有属性对应的属性描述符。 Object.getOwnPropertyDescriptor(obj,prop) ●obj: 需要查找的目标对象。 ●prop:目标对象内属性名称( String类型)。 ●返回值:如果指定的属性存在于对象上,则返回其属性描述符对象,否则返回undefined。 "注意:如果该方法的第一个 参数不是对象,会报错(TypeError)。" var obj= {} obj.attr= 'jine'; /* {value:“jine”,writable: true, enumerable: true, configurable: true} */ console.log(Object.getOwnPropertyDescriptor(obj,'attr')); "返回的默认是数据描述符" getOwnPropertyNames()方法 "可以循环遍历对象中可被枚举和不可被枚举的属性"
设置属性描述符
1. Object.defineProperty()方法为对象定义新属性或修改现有属性, 并返回该对象。 Object.defineProperty(obj, prop, descriptor) ●obj: 要在其上定义属性的对象。 ●prop:要定义或修改的属性的名称。 ●descriptor: 将被定义或修改的属性描述符。 ●返回值:被传递给函数的对象。 例: var obj={ name:"jine" } Object.defineProperty(obj,'name',{ value:"Ren" }); console.log(obj.name); /* 结果为:Ren */ "注意:如果直接使用对象名.属性名=值,那么当前属性描述符默认为可修改、可删除、可枚举" "如果使用Object.defineProperty()方法,该新属性描述默认都为false,也就是不能修改、删除、可枚举",当然也可以用writable、configurable、、value、enumerable等设置为true ----------------------------------------------------------------------------- 2. Object.definePropecties()方法为对象定义一个或多个新属性或修改现有属性,并返回该对象。 Object.defineProperties(obj, props) ●obj: 要在其上定义属性的对象。 ●props:要定义其可枚举属性或修改的属性描述符的对象。 ●返回值:被传递给函数的对象。
属性描述符的存储器
第一种写法: var obj={ name:"jine" } var value Object.defineProperty(obj,'name',{ get:function(){ console.log("get function") return value }, set:function(newValue){ console.log("set function") value=newValue } }); obj.name="ren" /* 输出结果: ren set function */ obj.name /* 输出结果: ren get function */ ----------------------------------------------------------------------------------------- 第二种写法: var obj={ get attr(){ return "jine"; }, set attr(value){ console.log("setter:"+value); } } console.log(obj.attr) /* jine */ obj.attr=100; /* setter:100 */
防篡改对象
定义的对象默认在任何时候、任何位置,无论有意义的还是无意义的都可以修改对象的属性或方法。 而这些篡改可能会影响对象的内置属性或方法,从而导致对象的正常功能可能无法使用。 JavaScript在ECMAScript5版本中新增了放置篡改对象的属性或方法的机制,共提供了以下三级保 护方式: 1. 禁止扩展:禁止为对象扩展新的属性或方法 2. 密封对象:禁止扩展新的属性或方法,禁止配置现有的属性或方法的描述符,仅允许读写属性的值。 3. 冻结对象: 禁止对对象执行任何修改操作。
禁止扩展
如果禁止为对象扩展新的属性或方法,需要修改对象属性的extensible为false。 ●Object.preventExtensions()方法用于设 置指定对象不可扩展,即不能新增属性或方法。 ●Object.isExtensible()方法判断一个对象 是否是可扩展的(是否可以在它上面添加新的属性)。 true:表示指定目标对象是可扩展 false:表示指定目标对象不可扩展 例: var obj={} Object.preventExtensions(obj) obj.name="jine" console.log(Object.isExtensible(obj)) /*false为禁止扩展*/ console.log(obj.name) /*结果为undefined*/
密封对象
密封对象,就是指禁止为对象扩展新的属性或方法,并且禁止修改现有属性的"描述符"。 ●Object.seal()方法用于封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置。 当前属性的值只要可写就可以改变。 ●Obiject.isSealed()方法判断一个对象是否被密封。 /*最简单的方法来生成一个密封对象,当然是使用Object.seal.*/ var sealed= {}; Object.seal(sealed); Object.isSealed(sealed); // === true /*一个密封对象同时也是不可扩展的.*/ Object.isExtensible(sealed); // === false ---------------------------------------------------------------------------------------- 例: "如下代码测试,结果重要 ***" var obj={name:"jine"} Object.seal(obj) obj.name="ren" obj.name /* 1、结果得出name属性值被更改为:ren */ obj.age=22 obj.age /* 2、结果为undefined,由此可知,被密封后的对象,可以用直接修改的方法,来修改属性值或方法值,但是不能新增属性和方法 */ console.log(Object.getOwnPropertyDescriptor(obj,"name")) /* 3、此时密封对象后的打印结果为: configurable: false enumerable: true value: "ren" writable: true 因其原来声明的对象,其中name的数据属性符都为true,但密封对象后只有configurable变为了false,那么因此当delete obj.name后,并不比起作用,可以看出configurable:flase;是生效了。 */ Object.defineProperty(obj,'name',{ value:"jine" }) /* 4、结果可以更改对象的属性值 */ Object.defineProperty(obj,'name',{ value:"jine", writable:false }) /* 5、结果可以更改,但需要注意的是,如若在此操作后在执行一次writable:true 便会报错 */ 总结:可以直接obj.name="jine" 修改属性或方法的值(但不能新增),而且也不能用defineProperty()来新增,defineProperty()还是可用来操作value和writable(只能改一次),也就是说可以来修改值或者改变writable属性描述符,但另外俩个不能修改 "注意:将对象进行密封后:1、不能为该对象新增属性或方法。2、不能进行修改的描述符为:configurable和enumerable "
冻结对象
冻结对象,就是指禁止对 对象执行任何修改操作。 ●Object.freeze()方法用于冻结一个对象,冻结指的是不能向这个对象添加新的属性,不能修改其已有属性的值,不能删除已有属性,以及不能修改该对象已有属性的可枚举性、可配置性、可写性。该方法返回被冻结的对象。 ●Obj.isFrozen()方法判断一个对象是否被冻结。 //使用0bject.freeze是冻结一个对象最方便的方法. var obj={name:"jine" }; Object.isFrozen(obj) //=== false,对象没有被冻结 Object.freeze(obj); // 开始冻结指定对象 Object.isFrozen(obj) //=== true,冻结对象 Object.isSealed(obj) //true,一个冻结对象也是一个密封对象. Object.isExtensible(obj)//false,当然,更是一个不可扩展的对象.
原型
在JavaScript中,函数是一个包含属性和方法的Function类型的对象。 而原型( Prototype)就是Function类型对象的一个属性。 在函数定义时就包含了prototype属性,它的初始值是一个空对象。null 在JavaScript中并没有定义函数的原型类型,所以原型可以是任何类型。 原型是用于保存对象的共享属性和方法的,原型的属性和方法并不会影响函数本身的属性和方法。 "JavaScript语言中,ECMAScript5之前原型链就是实现继承的默认方式" function fun(){ name='jine' console.log(name) } console.log(fun.prototype) /*结果为空对象:fun{} */
获取原型
通过如下两种方式可以获取对象的原型,从而设置共享的属性和方法: ●通过构造函数的prototype属性。 function Person() { console.log('Person instantiated'); } console.log(Person.prototype); console.log(Person["portotype"]); /*俩种方式结果一样,*/ ●通过Object对象的getPrototypeOf( obj )方法。 function Person() { console.log('Person instantiated'); } console.log( Object.getPrototypeOf( Person) );
为原型新增属性和方法
function fun(){ console.log("hha") } fun.prototype.name="jine" /*第一种方式*/ Object.defineProperty(fn.prototype,'age',{ value:22, enumerable:true }) /*第二种方式*/
为原型新增属性或方法的写法扩展
自有属性与原型属性
●自有属性:通过对象的引用添加的属性。其它对象可能无此属性;即使有,也是彼此独立的属性。 ●原型属性:从原型对象中继承来的属性,一旦原型对象中属性值改变,所有继承自该原型的对象属性均改变。 例如: function fun(name){ this.name=name this.print=function(){ console.log(this.name) console.log(this.age) } } fun.prototype.age=22 fun.prototype.printf=function(){console.log("我是原型方法")} /* 为fun构造函数设置原型age属性(此时便为原型属性) 为fun构造函数设置原型printf方法(此时便为原型方法) */ var jine=new fun("jine") jine.print() /* 构造jine对象,并打印,结果为:jine 22 (此时的jine便为自有属性,而22便为原型属性) */ var ren=new fun("ren") ren.print() /*构造ren对象,并打印,结果为:ren 22 */ jine.printf() ren.printf() /*调用原型方法*/
覆盖原型属性
----------------------------------------------------------------------------------------- 深入练习1,例如: function fun(name){ this.name=name this.print=function(){ console.log(this.name) console.log(this.age) } } fun.prototype.age=22 var jine=new fun("jine") jine.age=20 /*将原型提供的属性覆盖了,但不会改变原型的属性*/ var ren=new fun("ren") jine.print() /*结果:jine 20*/ ren.print() /*结果:ren 22 所以说操作对象,并不会改变原型中的属性和方法 */ ----------------------------------------------------------------------------------------- 深入练习2,例如 function fun(name){ this.name=name this.age=18 this.print=function(){ console.log(this.name) console.log(this.age) } } fun.prototype.age=22 var jine=new fun("jine") jine.age=20 var ren=new fun("ren") jine.print() /*结果:jine 20 覆盖掉了构造函数提供的age属性和原型的age属性 */ ren.print() /*结果:ren 18 被构造函数提供的age属性覆盖了,并没有改变原型 */ console.log(fun.prototype) /*结果看出原型的属性并没有更改*/
检测自有属性或原型属性
●使用hasOwnPrototype()方法"检测对象是否具有指定的自有属性" function Hero(){} Hero.prototype.name="jine" var hero = new Hero() console.log(hero.hasOwnProperty("name")) /* 结果为:false(不存在自有属性。可能是原型属性,可能不是。这个方法只是用来判断是否有自有属性), 如若为true,便是自有属性。 */ ●使用in关键字检测对象及其原型链中是否具有指定属性(自有属性或原型属性): function Hero(){} Hero.prototype.name="jine" var hero = new Hero() console.log( "name" in hero ); /* 结果为:true(有指定属性,可能为自有属性,也肯为原型属性) */
显式原型与隐式原型
function Fun(name){ this.name=name } Fun.prototype.age=22 var jine=new Fun("jine") console.log(Fun.prototype) console.log(jine.__proto__) prototype(函数的原型,也称为显式原型) __proto__ (对象的原型,也称为隐式原型)
isPrototypeOf()方法
用来判断一个对象是否是另一个对象的原型 var obj={ name:'jin' } function Fun(){} Fun.prototype=obj var person=new Fun() obj.isPrototypeOf(person) /*true*/
扩展内置对象
1、第一种写法 例如: console.log(Object.prototype) /*{}*/ Object.prototype.print=function(){ console.log("我是扩展的方法") } var person=new Object() person.print() /*我是扩展的方法*/ "这样扩展了内置的对象,随着js版本更新可能会和内置对象引起冲突" 2、第二种写法 例如: Object.defineProperty(Object.prototype,'print',{ value:function(){ console.log("我是扩展的方法") } }); var person=new Object() person.print() ----------------------------------------------------------------------------------------- 扩展: Array.prototype.inArray=function(color){ for(var i=0,len=this.length;i<len;i++){ if(this[i]===color){ return ; } } return false; } var a=["red","green","blue"]; console.log(a.inArray("red")); /*true*/ console.log(a.inArray("yellow")) /*false*/
原型链
个人理解: /* * @Author: Jine * @Date: 2020-06-07 22:56:18 * @Last Modified by: Jine * @Last Modified time: 2020-06-08 21:01:21 * * 代码内容: * 原型链图片验证,因为很多文章或者每个人的理解不同,本人也不是很明白 * 而且本人老是钻牛角尖,所以希望能用代码可以反推 * 下面代码是自身结合原型链图片简单测试 * 得到的理论,部分来自代码测试结果,部分来自众多文章中共鸣的结果 */ /* 1、__proto__和prototype关系 */ function Foo(){ this.name="jine" } Foo.prototype.age=22 var foo=new Foo() console.log(Foo.prototype) /*结果:Foo { age: 22 } */ console.log(foo.__proto__) /*结果:Foo { age: 22 } */ console.log(Foo.prototype===foo.__proto__) /* 结果: true 结论:Foo构造函数的prototype原型对象和foo对象的__proto__是相等的,都指向了同样的原型对象 补充知识: prototype(构造函数的原型,也称为显式原型) __proto__ (对象的原型,也称为隐式原型) */ /* 2、constructor */ console.log(Foo.prototype.constructor===Foo) /* 结果:true 结论:Foo构造函数的原型对象的constructor指向Foo构造函数 每个构造函数都会默认构造出原型对象,而原型对象的constructor属性指向的是实例化本原型对象的构造函数 */ /* 3、对象函数(function Object)和普通函数(function Function)的指向关系 */ function fun(){ console.log("我是一个普通函数") } console.log(fun.__proto__) /* 普通函数结果:[Function] 结论:普通函数也能用 __proto__属性,说明普通函数也是一个特殊的对象 其原型的__proto__指向的是Object.prototype */ console.log(Foo.__proto__) /* 对象函数结果:[Function] 结论:对象函数的原型是Function.prototype,说明对象函数也是一个特殊的函数 */ console.log(fun.__proto__===Foo.__proto__) /* 结果:true 结论:普通函数function Function()和对象函数function Object()的原型都是Function.prototype */ console.log(fun.__proto__.__proto__===Foo.prototype.__proto__) /* 结果:true 结论:此时函数和对象指向的是Object.prototype */ console.log(Foo.prototype.__proto__.__proto__) /* 结果:null 结论:所有对象的最终原型都是空对象 */ console.log(foo instanceof Object) console.log(Foo instanceof Object) console.log(Foo instanceof Function) console.log(fun instanceof Function) console.log(fun instanceof Object) console.log(foo.__proto__ instanceof Object) console.log(Function.__proto__ === Function.prototype); console.log(Function.prototype.constructor === Function); console.log(Function.prototype === Function.prototype); /*以上结果都为true */ /* 总结: 虽然这些代码也不能完全证明Function和Object关系,但也可以从中获取收益 1、所有对象都是Object的实例,并继承Obejct.prototype的属性和方法 2、Obejct.prototype是所有对象的原型 3、__proto__和constructor属性是对象所独有的 4、prototype属性是函数所独有的,因为函数也是一种对象,所以函数也拥有__proto__和constructor属性。 5、通过__proto__属性将对象连接起来的这条链路即我们所谓的原型链。 6、constructor属性的含义就是指向该对象的构造函数 7、Object.prototype.__proto__最终原型为null 8、函数也是对象,只不过是具有特殊功能的对象而已。任何函数都可以看做是通过Function()构造函数的new操作实例化的结果 Function与Object的三角关系: 可以分为俩种角度看: 1、Object角度: Foo.prototype是foo的原型对象,同时自身也是实例的对象 因为任何对象都可以看作是通过Object()构造函数new实例化的对象 所以Foo.prototype作为实例对象,它的构造函数是Object() 而其原型对象便是所有对象的原型,Object.prototype 实例对象Foo.prototype的proto属性同样指向原型对象Objec.prototype 而Obejct.prototype的constructor属性指向实例自己的构造函数Object 2、Function角度: 函数也是对象,任何函数都可以看作通过Function()构造函数new实例化的结果 那么,Function可以看成是调用其自身的new操作的实例化的结果 把函数Foo当作实例化对象时,其构造函数Function,所以其__proto__指向Function.prototype 如果Function.prototype作为实例对象的话,其原型对象可以看成是Object()构造函数的new操作的实例化结果。 所以,Function.prototype的原型对象是Object.prototype,其原型函数是Object() 最后的总结: 总体来说吧,JavaScript原型链很乱, 如果把它想简单:函数也是个对象,对象也是个函数,万物皆为对象,所有对象的原型的最终节点都为null 如果把它想复杂:通过表象很难找到正确答案,越饶越乱,可能最终要涉及到更为底层原理,而实际开发运用不多 */
原型链补充
function A(){ this.a='a' } /*创建a对象*/ var a=new A() function B(){ this.b='b' } /*B构造函数的原型指向对象a*/ B.prototype=a /*创建b对象*/ var b=new B() function C(){ this.c='c' } /*C的构造函数的原型指向对象b*/ C.prototype=b /*创建b对象*/ var c=new C() console.log(a.a) /*a*/ console.log(a.b) /*undefined*/ console.log(a.c) /*undefined*/ console.log(b.a) /*a*/ console.log(b.b) /*b*/ console.log(b.c) /*undefined*/ console.log(c.a) /*a*/ console.log(c.b) /*b*/ console.log(c.c) /*c*/ /*以上为了实现继承关系而不是共享,而单独创建对象,会对效率有影响*/ ----------------------------------------------------------------------------------------- 如下为c对象完成继承,而优化代码: function A(){} A.prototype.a="a" /*给A构造函数的原型添加a属性*/ function B(){} /*将B的原型指向A的原型*/ B.prototype=A.prototype /*B的原型添加b属性,此时已有了a属性 若把代码顺序更改,先给B的原型赋值b属性后,再将A的原型赋值到B的原型,会产生原型空间指向被更改问题(开始指向的内存空间,会被第二此赋值更改为指向A的原型内存空间) */ B.prototype.b='b' /*这样虽然效率优化,但写法便被固定*/ function C(){ this.c='c' } C.prototype=B.prototype; var c=new C() console.log(c.a) /*a*/ console.log(c.b) /*b*/ console.log(c.c) /*c*/
原型链问题
1、原型链实际上是在多个构造函数或对象之间"共享属性和方法" 以下代码实现了原型共享而不是单向的继承,例如: function A(){} A.prototype.a="a" function B(){} B.prototype=A.prototype B.prototype.b='b' function C(){} C.prototype=B.prototype; C.prototype.c='c' var c=new C() console.log(c.a) /*a*/ console.log(c.b) /*b*/ console.log(c.c) /*c*/ var a=new A() console.log(a.a) /*a*/ console.log(a.b) /*b*/ console.log(a.c) /*c*/ var b=new B() console.log(b.a) /*a*/ console.log(b.b) /*b*/ console.log(b.c) /*c*/ /* 以上的a,b,c的原型,都是指向同一个内存空间,最终指向的都是A构造函数的原型 */ ----------------------------------------------------------------------------------------- 2、创建子类对象时,不能向父级的构造函数传递任何参数
原型式继承
/* * @Author: Jine * @Date: 2020-06-07 17:07:20 * @Last Modified by: Jine * @Last Modified time: 2020-06-07 17:30:02 */ /* 定义一个函数:用于实现对象之间的继承 参数: obj:继承关系中的父级对象(也就是当前对象的原型) prop:继承关系中的子级对象的属性和方法(也就是自有属性和方法) */ function fun(obj,prop){ function Fun(){ for(var i in prop){ this[i]=prop[i] /*this[i]:给当前对象添加属性 prop[i]:获取传入对象属性的值 */ } } Fun.prototype=obj return new Fun(); } var result=fun({name:"jine"},{age:18,hell:22,se:23}) console.log(result) console.log(result.name)
Object.create() 实现继承
语法:Object.create(proto[, propertiesObject]) 参数: proto 新创建对象的原型对象。 propertiesObject可选。如果没有指定为 undefined,则是要添加到新创建对象的不可枚举(默认)属性(即其自身定义的属性,而不是其原型链上的枚举属性)对象的属性描述符以及相应的属性名称。 这些属性对应Object.defineProperties()的第二个参数。 返回值:一个新对象,带着指定的原型对象和属性。 例: /* * @Author: Jine * @Date: 2020-06-07 17:42:31 * @Last Modified by: Jine * @Last Modified time: 2020-06-07 17:48:13 */ var newObj=Object.create({name:"jine"},{ age:{ value:18, writable:false } }) console.log(newObj) /*{} */ console.log(newObj.name) /*jine */ console.log(newObj.age) /*18 */
借助构造函数实现继承
无论是原型链还是原型式继承,都具有相同的问题。 想要解决这样的问题的话,可以借助构造函数(也可以叫做伪造对象或经典继承)。 这种方式实现非常简单,就是在子对象的构造函数中调用父对象的构造函数。 具体可以通过调用apply()和call()方法实现。 apply()和call()方法都允许传递指定某个对象的this。 对于继承来讲,可以实现在子对象的构造函数中调用父对象的构造函数时,将子对象的this和父对象的this绑定在一起。
组合方式继承
组合继承,也叫做伪经典继承,指的是将原型链或原型式继承和借助构造函数的技术组合在一起,发挥二者长处的一种继承方式。 具体实现的思路就是: ●使用原型链或原型式继承实现对原型的属性和方法的继承。 ●通过借助构造函数实现对实例对象的自有属性的继承。 这样,既通过在原型上定义方法实现了函数的重用,又可以保证每个对象都有自己的专有属性。 例如: /* * @Author: Jine * @Date: 2020-06-07 19:06:23 * @Last Modified by: Jine * @Last Modified time: 2020-06-07 19:14:54 */ /*构造函数的自有属性*/ function Parent(){ this.parent="parent" } /*Parent的原型属性 */ Parent.prototype.age=22 /*构造函数的自有属性*/ function Child(){ this.child="child" Parent.call(this) } /*将Child的原型属性指向Parent原型属性 */ Child.prototype=Parent.prototype var child=new Child() console.log(child) /*Parent { child: 'child', parent: 'parent' } 此时child对象的原型为Parent */ console.log(child.age) /*22*/ /*以上结果可看出,child对象即继承了父级的原型属性,也继承了父级的自有属性 */
错误与异常
错误,指程序中的非正常运行状态,在其它编程语言中称为“异常”或“错误”。 解释器会为每个错误情形创建并抛出一个Error对象, 其中包含错误的描述信息。 通过使用JavaScript提供的异常处理语句,可以用结构化的方式来捕捉发生的错误,让异常处理代码与核心业务代码实现分离。 错误与异常处理在应用中的重要性是毋庸置疑的。任何有影响力的Web应用都需要一套 完善的错误处理机制。
try…catch 语句
try 语句标记一块待尝试的语句,如果该语句出现错误,则通过catch语句进行捕获 try{ /*可能出错的代码 类似于if */ }catch(error){ /*在错误发生时的处理*/ }finally{ /*catch语句无法处理try语句中错误或异常时,所执行 类似于else */ } 打印try语句中出现错误的信息 例如: try{ console.log(v) }catch(error){ console.log(error) } /*可以传error,也可以传入别的参数*/
throw语句
function fun(arg){ if(arg){ return arg }else{ throw 'undefined' } } fun() /* throw 语句 :人为抛出错误或异常(可以是自定义的任意类型的内容) */ ----------------------------------------------------------------------------------------- 用捕获来接收throw抛出的错误,例如: try{ fun() }catch(error){ console.log(error) /* undefined 上面报错后,throw抛出的内容 */ }
嵌套 try…catch语句
错误类型
执行代码期间可能会发生的错误有多种类型,每种错误都有对应的错误类型。 当错误发生时,就会抛出对应类型的错误对象。 Error是基本错误类型,其他错误类型都继承自该类型。 Error类型的错误很少见,如果有也是浏览器抛出的。 这个基本错误类型的主要目的是提供给开发人员抛出自定义错误的。 ----------------------------------------------------------------------------------------- JavaScript还提供了7种预定义的错误类型,有以下: EvalError 表示错误的原因:与eval()有关。 InternalError 表示Javascrip引擎内部错误的异常。 RangeErcor 表示错误的原因:数值变量或参数超出其有效范围。 ReferenceError 表示错误的原因:无效引用。 SyntaxError 表示错误的原因: eval()在解析代码的过程中发生的语法错误。 TypeError 表示错误的原因:变量或参数不属于有效类型。 URIError 表示错误的原因:给encodeURI()或 decodeURI()传递的参数无效。
this 关键字
它是一个很特别的关键字,被自动定义在所有函数的作用域中。 实际上,JavaScript中this的机制并没有那么先进,但是开发者往往会把理解过程复杂化。 不理解它的含义,大部分开发任务都无法完成。 this都有一个共同点,它总是返回一个对象。 简单说,this就是属性或方法“当前”所在的对象。
调用位置
想要了解"this"的绑定过程,首先要理解调用位置: "调用位置"就是函数在代码中"被调用的位置(而不是声明的位置)。" 通常来说,寻找调用位置就是寻找“函数被调用的位置”。 最重要的是要分析调用栈(就是为了到达当前执行位置所调用的所有函数)。 在全局中调用,例如: var v=100 function fun(){ console.log(this.v) } fun() /* this指向那个对象,取决于调用的位置 此时调用的fun(),是在全局作用域中(存在一个全局对象,定义的全局变量或函数都是全局对象的属性和方法) 1、在浏览器环境中:全局对象为window,那此时fun()的调用,相当于:window.fun() 所以此时的this指向的是window对象,输出结果便为:100 2、在node.js环境中:全局对象为Global,此时的调用为:Global.fun(),此时的this指向的是Global对象, 但在node.js中Global全局对象不可直接调用,此时的输出结果便为:undefined */ 在另外一个对象中当作方法来调用,例如: var obj ={ v:200, f:fun } obj.f() /* 输出结果为:200 此时的调用位置,是在obj对象中,被当成obj的方法来调用 所以此时的this为obj对象 */
绑定规则
默认绑定
在一个函数体中使用this,当该函数被独立调用。 可以把这条规则看作是无法应用其他规则时的默认规则。 function foo() { console.log( this.a ); } vara= 2; foo(); // 2 声明在全局作用域中的变量(比如var a = 2)就是全局对象的一个同名属性。 当调用foo()函数时,this.a被解析成了全局变量a。 函数调用时应用了this的默认绑定,因此this指向全局对象。
隐式绑定
隐式绑定的规则需要考虑的是调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含。 当然,这种说法并不准确。 function foo() { console.log( this.a ); }var obj= { a: 2, foo: foo }; obj.foo(); // 2 调用位置会使用obj上下文来引用函数,因此你可以说函数被调用时obj对象“拥有”或者“包含”它。
隐式丢失
隐式丢失是最常见的this绑定问题,指的就是被隐式绑定的函数会丢失绑定对象, 也就是说它会应用默认绑定,从而把this绑定到全局对象。 var v=100; /*全局变量v*/ function fn(){ console.log(this.v) } var obj={ v:200, f:fn /*将对象的f()方法指向fn()函数*/ } var fun=obj.f /*定义一个全局变量fun来接收,obj.f方法*/ fun() /*调用*/ /* 结果:浏览器环境中输出为:100,node.js环境中输出为:undefined 结论:此时的this指向的是全局对象,所以出现了隐式丢失 原因:虽然fun变量接受到了obj.f的方法,但只是引用赋值操作,并没有将obj.f()方法返回结果赋值,所以此时在调用fun()后,会通过obj.f的指向,去找到了fn函数,便开始执行,此时的this是间接的通过fun()调用,而fun也在全局作用域中,所以this指向的是全局对象 */
显示绑定
显式绑定就是明确在调用时,this所绑定的对象。 JavaScript中提供了apply()方法和call()方法实现, 这两个方法的第一个参数接收是一个对象,会把这个对象绑定到this,接着在调用函数时指定这个this。 var v=100; /*全局变量v*/ function fn(){ console.log(this.v) } var obj={ v:200, f:fn /*将对象的f()方法指向fn()函数*/ } var fun=obj.f /*定义一个全局变量fun来接收,obj.f方法*/ fun.apply(obj) /*结果为:200*/ /*此时通过apply或者call方法明确的传入了this绑定的对象,解决了隐式丢失,但缺点是不如隐式绑定灵活*/ 如果传入了一个原始值来当作this的绑定对象,这个原始值会被转换成它的对象形式,这通常被称为“装箱”
new 绑定
在JavaScript中,构造函数只是一些使用new操作符时被调用的函数。 包括内置对象函数在内的所有函数都可以用new来调用,这种函数调用被称为构造函数调用。 使用new来调用函数,会自动执行下面的操作: 1. 创建(或者说构造)一个全新的对象。 2.这个新对象会绑定到函数调用的this。 3.如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。 function fun(name){ this.name=name console.log(this.name) } var jine=new fun("jine") /*此时的this 指向的是这个jine对象 */ var person=new fun("person") /*此时的this 指向的是这个person对象 */
严格模式
严格模式是、JavaScript中的一种限制性更强的变种方式。 严格模式不是一个子集:它在语义上与正常代码有着明显的差异。 不支持严格模式的浏览器与支持严格模式的浏览器行为上也不一样,所以不要在未经严格模式特性测试情况下使用严格模式。 严格模式可以与非严格模式共存,所以脚本可以逐渐的选择性加入严格模式。 首先,严格模式会将JavaScript陷阱直接变成明显的错误。 其次,严格模式修正了一些引擎难以优化的错误:同样的代码有些时候严格模式会比非严格模式下更快。 第三,严格模式禁用了一些有可能在未来版本中定义的语法。
开启严格模式
在JavaScript中想要开启严格模式,需要在所有代码之前,定义一个不会赋给任何变量的字符串: "use strict";//或者'use strict'; 如果之前的JavaScript代码是非严格模式的话,建议不要盲目为这段代码开启严格模式,这样可能会出现问题。 建议按一个个函数去开启严格模式(至少在学习的过渡期要这样做)。 "注意:可以在全局作用域中开启,也可以定义在局部作用域单独开启"
变量
意外创建变量
function fun(){ v=100 console.log(v) } fun() console.log(v) /* 在函数作用域中定义变量,且是不使用var关键字,那么其变量会自动提升为全局变量 所以在全局作用域中也可以访问v变量 这是非严格模式的问题,所以要开启严格模式来避免 */
静默失败转为异常
const A=3.14 var A=2 console.log(A) /* 有的环境中只会出现异常,而不是报错,所以要开启严格模式 静默失败还有好多,在非严格模式下,有的问题不会报错 */
禁用delet关键字
'use strict' var v=100 delete v; console.log(v) /* 严格模式下,禁用delete关键字(仅针对删除变量,数组和对象属性不影响) 若不不开启严格模式,将不会报错,而且还会打印出100 */
对变量名的限制
在严格模式下,JavaScript对变量名也有限制。特别不能使用如下内容作为变量名: implements interface let package private protected public static yield 上述内容都是保留字,在ECMAScript的下一个版本中可能会用到它们。 在严格模式下,使用上述标示符作为变量名会导致语法错误。
对象
不可删除的属性
'use strict' delete Object.prototype console.log(Object.prototype) /* 若在非严格模式下,不会报错,但也没有删除 若在严格模式下,直接报错,所以不能删除Object.prototype */
对象属性名重复
'use strict' var obj={} Object.defineProperty(obj,'name',{ value:'jine', writable:false, configurable:false, enumerable:false }) obj.name='ren' delete obj.name console.log(obj.name) /* 非严格模式下,这并不会报错,结果也还为jine,name属性并没有被修改 这也是一种静默失败例子, 严格模式下,这便会直接报错,当然name属性也是不可修改的 */
只读属性的赋值
'use strict' var obj={} Object.defineProperty(obj,'name',{ value:'jine', writable:false, configurable:false, enumerable:false }) obj.name='ren' console.log(obj.name) /* 非严格模式下,这并不会报错,结果也还为jine,name属性并没有被修改 这也是一种静默失败例子, 严格模式下,这便会直接报错,当然name属性也是不可修改的 */
不可扩展的对象
'use strict' var obj={} Object.preventExtensions(obj) obj.name='jine' console.log(obj) /* 这也是一种静默失败, 非严格模式并不会报错,但也不会新增name属性 严格模式下,直接报错 */
函数
参数形参名必须唯一
'use strict' function sum(a,a,b){ return a+a+b } sum(1,2,3) /* 若不开启严格模式:,此时a取2,b取了3,结果为7 若开启严格模式,直接报错 */
arguments的问题
'use strict' function fun(value){ var value='jine' console.log(arguments[0]) } fun("ren") /* 非严格模式:arguments对象获取的值与形参有关,结果为:jine(如果局部变量与形参名相同,根据就近原则进行获取) 严格模式下:arguments对象获取的值与形参无关,结果为:ren */
arguments.callee()方法不能调用
'use strict' function fun(){ return arguments.callee } fun() /* 非严格模式:不报错 严格模式:arguments对象无法调用callee方法 */
函数声明的限制
在严格模式下,只能在全局域和函数域中声明函数 'use strict' for(var i=0;i<3;i++){ var v=100 console.log(v+i) function fun(){ console.log("this is function !") } } fun() /* *严格模式:结果为:fun is not defined,不允许在块级作用域中声明函数 *非严格模式:可以这样声明函数 */
eval() 函数
'use strict' eval("var v=100;") console.log(v) /* *严格模式下:增加eval作用域,eval()函数中定义的变量只能在当前eval()函数中使用 * 非严格模式:打印结果为:100 */
禁止读写
在严格模式下,禁止使用eval()和arguments作为标示符,也不允许读写它们的值。 使用var声明。 赋予另一个值。 尝试修改包含的值。 用作函数名。 用作命名的函数的参数。 在try..catch语句中用作例外名。
在严格模式下,以下的所有尝试将导致语法错误: "use strict";//开启严格模式 eval= 17; arguments++; ++eval; var obj={ set p(arguments){}}; var eval; try{ } catch (arguments){ } function x(eval) {} function arguments() {} vary = function eval() { }; varf = new Function("arguments", "'use strict; return 17;");
抑制 this
'use strict' var v=100; function fun(){ console.log(this.v) } var obj={ v:200 } fun() fun.call(obj) fun.call(null) fun.call(undefined) /* 非严格模式:使用apply()或call()方法,null或undefined值会被转为全局对象 严格模式:函数的this值始终是指定的值,如若使用apply()或call()方法,必须指明对象,传入null或undefined直接报错 */
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算