当为数组、对象等时,使用typeof返回的都为object,所以需要使用instanceof来判断具体的类型,并且只能和引用类型使用,基本类型不能使用该方法
js没有块级作用域
js变量:$ _ 字母和数字组成,不能以数字开头
jquery中 $==jquery
underscore:以_开头
js中区分大小写,保留字:程序中保留用作关键字,可以在之后的功能可以实现为关键字
js中变量的命名要有意义,要一致
使用下划线或者驼峰式
btn_confirm btn_cancel
btnConfirm btnCancel
var score; 变量的声明
数据类型和堆栈
区别就是引用类型的值可以修改,而基本类型的值不能修改,例如:
字符串方法都是返回新的字符串,而原字符串并未改变
person是一个引用类型,而person的值发生了变化
基本类型不能添加属性 输出undefined
但是上面基本类型字符串却可以使用replace属性方法,该方法哪来的?
基本类型调用方法会找到相应的包装对象,
1--->Number对象
"adf"-->String对象
它们的方法和属性都在包装对象上
内存分为堆内存和栈内存两种,栈的大小是固定的,堆内存的空间大小不固定
而地址值的大小都是一样的,所以将引用类型的地址保存到栈中,并命名,可以更好的定位到堆的位置,同时堆中的大小可以变化,
所以栈内存中如果是基本类型会直接返回值,引用类型的话返回地址值,然后再去堆内存中访问相应的值,这也就是变量的两种访问方式,按值访问和按引用访问。
输出false
用来判断引用对象的内容是否一样
遍历数组是否相等
复制对象,浅拷贝 ,两个对象
为什么是浅拷贝?
原因:
当有引用类型时,它的复制为基本数据类型的复制,所以直接将family的地址值复制过去,结果指向同一个family的堆空间
jquery的$.extend()方法既可以浅拷贝又可以深拷贝(???),
深拷贝涉及函数递归的知识
引用类型的参数传递的是地址值,
Person.name输出的为xm
上述代码:person指向一个空对象1,调用setName()方法,person的地址值赋值给obj,obj也指向这个空对象1,将其名字设置为xm;然后obj指向空对象2,将空对象2的name设置为xh,但是person的指向未发生改变,仍然指向对象1
返回false,instanceof只能和引用类型连用,不能和基本类型连用
全局作用域和局部作用域:
变量的作用域:变量赖以生存的环境,1.它的生命周期 2.在哪里可以访问到它
js中的局部作用域指的是函数作用域,因为js中没有块级作用域
全局作用域:当程序执行完之后,全局变量才会失效,而局部变量则是函数执行完就会失效
全局中的属性和函数都属于window对象的,window为全局作用域的变量对象,
fn作用域也存在一个变量对象 拥有sex属性和fn2()方法,js引擎可以使用到
fn2 作用域也同理,也存在一个变量对象
注意:console.log(person) 报错:person不存在
console.log (window.person) 当它变成属性的时候则不报错,输出为undefined
作用域链:从内到外,作用域链的尾部是window,
查找一定是沿着作用域链的,在查找的作用域内先查找相应的,如果未找到,则向外一层作用域查找,直到找到window
当作用域很长的时候,局部变量的查找速度快于全局变量,所以在jquery源码中将window对象通过参数传递到函数里面,一个原因就是局部的速度更快,
可以使用with延长作用域链,但是不要使用
with生成person作用域,在person作用域内相当于有一个person变量对象,而name,sex,score则为它的属性,name=person.name (类似window作用域中的name=window.name)
JS解析机制:
打印xh
此时打印为undefined
上面代码:
window下面有:
name
age
fn
fn变量对象下面有:
name
age
arguments(参数相当于局部变量)
预解析:分别在两个作用域内查找var字样
将其值定为 undefined
window: name=undefined age=undefined (两个var)
再接着查找全局范围内的function关键字,直接拿过来,不是赋值
function fn(arguments){
console.log(name);
name='xm';
age=10;
}
所以预解析的时候函数就已经声明了
全局作用域解析完
局部作用域:
fn:
name=undefined
age=undefined
arguments=undefined
预解析完开始逐行读代码:
灰色部分不用再读了,预解析时已经声明了,直接fn()
此时console.log(name) 所以输出undefined
预解析冲突:
当变量名=函数名时,此时变量name就被函数name给干掉了,
此时预解析则是后面一个函数有效
注意:在代码块内的函数可能不能预解析,所以不要在代码块内定义函数
1中输出undefined
2中报错,虽然a为全局变量,但是没有var 字样,不能进行预解析,在逐行解析时,在一行时,a没有定义,所以报错
预解析:var a=undefined (XXX)
function a()后一个生效
逐行解析:输出a() 1 1 3 3 报错
最开始预解析完后a为一个函数名,然后接着将它赋值为1 和3 (此时function函数声明部分不会再逐行解析,只有函数执行的时候才会执行它的代码)
因为函数被赋值为了数字,所以a()就会报错,此时不能当成函数来执行
js解析是分标签进行的,例子:
此时报错,因为先解析上面一个script标签,而a未声明,所以报错
此时上面标签预解析过了,也逐行解析过了,所以此时输出1
输出undefined 1
输出1 2
输出:undefined 1
函数中的参数相当于局部变量,fn(a)的a也有预解析,var a =undefined
输出:1 1
a=1作为参数传递过去。所以在函数内部console.log(a)时为1
垃圾收集机制:
标记清除的方法:将内存中的所有变量都打上标记,当变量还没有离开它的环境时,就把它上面的标记清除,还剩标记的即为离开了环境的变量,垃圾回收机制就清除它们的值,回收它们所占的内存
引用计数:不常用,当值的引用次数为0时,就回收其内存空间,例子:
此时{name:'xm',age:18}对象的引用次数为2,
而xh,xm都指向新的变量之后,{name:'xm',age:18}对象的引用次数变为0,不能访问到它,它就可以被回收了
引用计数会造成循环引用
函数执行完后相当于xm=null,xh=null,但是它们的引用次数都是变为1,不为0,不会回收
IE中此时也出现循环引用
解决:
将它们再次-1 ,即为0,此时就可以回收了
内存管理:
将没用的数据设置为null,解除引用,适合大量的全局变量
js函数深入讲解:
对象的创建方式:
1.字面量创建方式:
2.构造函数创建对象(下面两种方式是等价的):
3.ECMscript5提供的:
删除之后输出undefined
in用来判断某个属性是否在对象中,在返回true,否则返回false
遍历对象:
使用for(var p in cat){}
Cat.p输出undefined,因为p是字符串,相当于cat."name",
应该使用cat[p];
函数:
函数的二象性:1 调用
2:函数也是对象
函数的两种定义方式:
函数也可以添加属性和方法
函数还可以:
add变量保存函数的地址值,就是函数本体,
第一个输出1
第二个输出函数function(){return 1;}
此时函数作为参数,传递到另一个函数中去,
fn函数名就是函数本体,加()则代表函数执行,所以上面传递的时候只要将fn函数本体传递就可以了
函数作为返回值,
新函数的调用等价于上面()()
所以把函数当做对象来使用
函数的三种定义方式:
Var add=functino fn(){}
此时在外部调用使用add(),不能使用fn(),而在内部使用fn()进行调用,fn相当于一个局部变量
使用构造函数时,参数一定要是字符串,第三个参数为函数体,也要为字符串
预解析过程中:几种方式有区别,可能会报错
此时都输出1
而使用赋值语句的形式声明函数:
输出1
但是写到前面时,报错,
预解析时,查找var,function字样,而function一定要在开头位置,才可以预解析到
此时报错,因为js解析器将其作为一个声明,声明就不能同时调用
所以改为不要用function开头,就可以调用匿名函数了,
也可以用括号括起来,也就不是function开头
方法的调用:
这也是方法的调用,将匿名函数赋值给document对象的方法onclick,浏览器帮我们调用该方法,
而下面这条语句则实现了调用,而不是浏览器的调用
’@‘特殊的名字属性(方法) 和调用使用[]
方法的链式调用:
构造函数的调用:
一定要使用new关键字,普通函数一定有返回值,当没有return时返回undefined,有return就返回return的值,而构造函数都会返回一个对象
函数的间接调用:每个函数下面都有apply方法和call方法,帮助我们间接调用函数
此时返回的是person.name的值,即为’xh‘
第一个参数是this指向,指向window对象,返回xm
apply()方法,只传一个参数时,同call方法,
call()的参数是一个一个的传,而apply()的参数只有两个,第一个为this的指向,而第二个为数组。
当得到的数据为数组时,使用函数的apply方法,就可以直接将整个数组传递过去,而不需要将数组内的值一个一个的传递参数
call和apply方法还可以判断数据的类型,是数组还是对象,….
链式调用:需要返回this对象
参数的类型:
参数传递的本质都是实参赋值给形参,当实参为引用时,形参和实参指向同一个地址的对象
参数个数:
实参>形参的情况:
arguments(类数组,它是一个对象)
arguments:
属性不是合法标识符时,需要使用引号引起来
arguments.callee 指代函数本体,一般在递归中使用
严格模式下不能使用arguments.callee,
就可以使用以下方法:
当函数名发生变化时,就不用更改里面的那个函数名
arguments.callee()加上“()”就可以调用,不加“()”时代表函数本体
arguments.length是实参的个数,add.length是形参的个数
throw手动抛出错误
什么可以作为参数:基本数据类型,数组,对象,函数
用对象做参数,当参数在三个以上时,使用对象做参数更好,
函数作为参数的例子:回调函数(不会立即调用,满足某些条件的时候就会调用)
可选参数时,要把可选参数放在后面
return:返回值
作为数组返回,可以返回多个值
返回对象
注意return的一个问题:
此时报错,因为编程风格问题