JS高级笔记
1.作用域问题
变量的寻找从当前域开始往外逐一寻找,直到找到全局域,如果没有找到,则为undefined。
var n = 1;
var s = ‘hello’;
var f = ‘sdhjsk’;
function fn(){
//js执行之前,存在着预编译,会给所有变量申请空间,但是不会
//赋值,因此在执行console.log(n);时,由于var n=3已经预编译 //了,但是并没有赋值,所有输出为undefined
console.log(n);//输出undefined
//首先在函数fn中寻找,发现没有s,再到函数外的域中寻找,找
//到var s = ‘hello’
console.log(s);//输出hello
//给n变量赋值,首先在fn域中寻找,有没有申明n变量
//发现没有,在函数外找到了var n = 1,因此又重新赋值 n=2
n = 2;
var n = 3;
var f = ‘fn f’;
function inner(){
console.log(n);//3
console.log(s);//hello
console.log(f);//undefined
var f = ‘inner f’;
}
inner();
}
fn();
2声明变量var的作用
function t(){
// var 表示申明,但是此处没有var,则直接是赋值,首先会在t函数域 中找是否有d变量,如果没有,则到更外层去找,如果都没有,则在window.d = 5,d因此成为了全局变量。
d = 5
}
3.js词法分析
3.1 js代码的整体运行分为词法分析期、运行期。
3.2 词法分析
第一步:先分析函数参数
第二步:再分析变量申明
第三步:分析函数声明
具体步骤:
0.在函数运行前的一瞬间,生成Action Object(活动对象)
1.1把函数声明的参数,形成AO的属性,参数的值为undefined
1.2接收实参,形成AO相应属性的值
2.分析变量的声明,例如var age,如果AO上已经有age属性,则不做
任何影响,如果没有Age属性,则添加AO属性,值是undefined
3.分析函数声明,如function foo(){ },则把函数赋值给AO.foo属性
注:如果此前有foo属性存在,则被覆盖了
例1
function t3(greet){
var greet = ‘hello’;
alert(greet);//hello
function greet(){
}
alert(greet);//hello
}
t3(‘hi’);
词法分析:
0.生成AO对象 AO={}
1.参数分析 AO={greet:undefined} –>AO={greet:’hi’}
2.变量申明分析,由于AO中有age属性,所有不做任何处理
3.函数greet将覆盖之前AO中的greet变量,因此AO={greet:function
greet(){}}
运行期:
var greet = ‘hello’;//给AO中greet属性赋值,AO={greet:’hello’}
alert(greet) //greet = AO.greet = ‘hello’;
alert(greet) //greet = AO.greet = ‘hello’;
例2
function a(b){
alert(b); //b函数
Function b (){
alert(b);//b函数
}
b();
}
a(1);
词法分析:
0.生成AO对象 AO={}
1.参数分析 AO={b:undefined} –>AO={b:1}
2.变量申明分析,没有变量申明
3.函数b将覆盖之前AO中的b属性, AO={b:function b(){}}
运行期:
alert(b); //b = AO.b = function b(){};
alert(b); //b = AO.b = function b(){};
例3
function a(b){
alert(b); //1
var b = function (){
alert(b);//b函数
}
b();
}
a(1);
词法分析:
0.生成AO对象 AO={}
1.参数分析 AO={b:undefined} –>AO={b:1}
2.变量申明分析,由于之前AO中有b属性,因此不做任何处理
3.没有函数申明,var b = function (){..}这句不是函数申明,算是赋值
语句,因此在运行期执行。
运行期:
alert(b); //b = AO.b = 1 输出1
var b = function (){} //AO={b:function(){}}
所有第二个alert(b)输出是函数b
4.arguments是什么?
4.1 arguments是一个对象,是一个长得很像数组的对象,arguments是函数运行时的实参列表。arguments.callee 表示当前运行的函数。
4.2函数运行时关键的三个对象
Action Object:在本函数AO上没有的属性,则会继续向外层函数AO 上找,直到全局变量,叫做作用域链。
Arguments:每个函数都有自己的callee,但是不向外层找arguments 的相关属性,即不形成链。
This 一句话,在函数中,谁调用该函数,this表示谁。
5.this到底是谁?
Js中函数的四种调用方法:
(1)普通函数调用:
function t(){
this.xx = 333;
}
t();//window调用函数t,所有this表示this,因此函数执行后
Window.xx = 333;
(2)作为对象的调用方法
Var Obj = {xx:22,yy:11,function t(){alert(this.xx)}};
Obj.t();//this表示obj对象,因此this.xx = obj.xx = 22;
(3)函数作为构造函数调用:
js中没有类的概念,创建对象是用构造函数来完成的,或直接用json格式{}来写对象。
function Dog(name,age){//构造函数
this.name = name;
this.age = age;
}
var dog = new Dog(‘huzi’,2);
//a.系统创建空对象{},把空对象的construtor属 性指向dog函数
/b.把函数的this指向该空对象
//c.执行该函数
//d.返回该对象
(4).函数被call或者apply调用 语法格式:函数.call(对象,参数
1,参数2)此时函数中的this是指的是参数中指定的对象。
有this的操作的函数,如(this.age = xx)的函数不能直接调用而是用new来调用因为直接调用,this表示的是window对象,会污染到全局变量。
6.闭包的概念:
function t1(){
var age = 20;
function t2(){
alert(age);
}
return t2;
}
var temp = t1();
var age = 99;
temp();//打印出来是20
在执行t1函数时,有申明了函数t2,由于t2函数能够访问到age=20
因此当t1函数返回t2时,不仅仅是返回的t2函数,而且还把它周围的变量环境一起返回,形成一个“环境包”,称为闭包。
一句话概括,函数的作用域取决于申明时,而不是调用时。
闭包计数器
多个人开发js程序,需要一个全局的计数器,多个人的函数共同用一个计数器,计数器一直增加。
第一版
function counter(){
var cnt = 0;
function cnter(){
return ++cnt;
}
return cnter;
}
var inc = counter();
inc();
inc();
第二版
var cnt = (function(){
var cnt = 0;
return function(){
return ++cnt
}
})();
第三版(在工作中,一般如何避免全局变量污染和冲突)
1.统一放在一个全局对象,如jquery->
//jQuery的计数器插件形式
.cnt = (function(){
var cnt = 0;
return function(){
return ++cnt
}
})();
alert($.cnt())
2.每个人用自己的命名空间 其实就是把自己的变量放在一个对象里
var yejiax = {}
yejiaxi.cnt = (function(){
var cnt = 0;
return function(){
return ++cnt
}
})();
7.Js的面向对象
7.1 在js中,有对象,没有类(但是有构造函数),在很多语言中,由类产生对象,但是js的对象不依赖于类。js的对象只是一个“属性字典”,因此我们可以直接造对象,不要类。js中的对象,就是一组属性与值得集合,属性可以任意增减,方法和属性不必区分。
7.2 用构造函数创建对象。
function Dog(){
this.leg = 4;
this.bark = function(){
alert(‘bark’);
}
}
var dog = new Dog();
以上并没有完成面向对象的封装,因为以上对象中的所有都是向外暴露的,
封装表示的是指向外面暴露接口,而里面的变量只能通过这些接口访问。
可以通过闭包的方式来对对象进行封装
function Dog(){
var age = 4//age只有调用getAge的方式获取
this.leg = 4;
this.bark = function(){
alert(‘bark’);
}
this.getAge = function(){
alert(age);
}
}
var dog = new Dog();
8.继承
8.1 js没有类的概念,因此js的继承是不是通过类来实现的
而是通过“原型”的概念来完成的的。
function tiger(){
this.bark = function(){
alert(‘我是百兽之王’);
}
}
var hu = new tiger();
function cat(){
this.climb = function(){
alert(‘我会爬树’);
}
}
var bosi = new cat();
//让tiger继承cat类,但是js做不到,我们明确的对tiger函数指定,用具体的cat对象作为老虎的原型,并创建老虎对象
tiger.prototype = new cat();
var huhu = new tiger();
huhu.climb();
老虎现在自身对象上寻找,没有爬树的方法,去找原型,原型cat对象上有此方法。
Object对象的原型指向null
Cat对象的原型指向普通Object对象
tiger对象的原型指向cat对象
*js的语法非常灵活,不仅可以原型继承,还可以用其他方法,如原型冒充,或复制继承
function Cat(leg,tail){
this.leg = leg;
this.tail = tail;
this.climb = function(){
alert('我会爬树');
}
}
function Tiger(leg,tail,color){
this.parent = Cat;
this.parent.apply(this,arguments);
delete parent;
this.color = color;
}
在用tiger造对象前,用Tiger的语句影响一个空对象{},在此过程中,tiger影响空对象前,先由Cat函数实施影响,因此最终得到的对象是由Cat和Tiger两者共同作用过的一个对象。
*复制继承 把父对象的所有属性,直接复制到自己的对象上
function Cat(leg,tail){
this.leg = leg;
this.tail = tail;
this.climb = function(){
alert('我会爬树');
}
}
function Tiger(color){
this.color = color;
this.extend = function(parent){
for(var key in parent){
this[key] = parent[key];
}
}
}
var tiger = new Tiger('red');
tiger.extend(new Cat());//老虎会爬树了
9.js中的静态方法
构造函数本身也是一个对象
function dog(age){
this.age = age;
}
new dog();//构造函数创造了一个dog对象,
这个构造函数本身也是一个对象。
比如:一个豆浆机,转动(new调用)时,能返回一杯豆浆(制造出的对象)
但豆浆机本身也是一台机器,也是一个对象
豆浆机身上也有属性–如开关,进水口等
function Hashiqi(){
this.bark = function(){
alert('wuwu');
}
}
Hashiqi.ajax = function(){//ajax是Hashiqi的静态方法
alert('ajax');
}
var h = new Hashiqi();//这个对象里面没有ajax方法
*ajax方法是属于函数本身的,和返回的对象没有关系
*bark的调用必须new Hashiqi得到对象,且由返回对象才能调用
*ajax方法要调用,不需要new对象,直接用Hashi器来调用
我们之前有没有接触过静态方法?
1.Math.random();静态方法
2.
.ajax();静态方法3.写jquery方法2中方法通过闭包把方法写到jquery上的原型上直接增加
对象的静态方法
.ajax//调用函数本身的
().ajax//调用对象的