JavaScript 进阶笔记

JavaScript 进阶篇

个人博客地址:https://blog.3xnb.com

在这里插入图片描述

面向对象

概述

面向对象编程的全称为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直接报错
*/
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值