为了账号安全,请及时绑定邮箱和手机立即绑定

JavaScript的初步了解

标签:
JavaScript
作用域

在js中,函数嵌套是非常普遍的。

在函数嵌套中,对变量是如何查找的?

  • 首先在函数内寻找,寻找不到,则往外层寻找...直到全局(window)区域

声明变量 var,用来定义变量。

注:以window.xxx引用全局变量,寻找不到,作为某个属性不存在,返回undefined;直接以xxx引用某变量,寻找不到,报xxx is not defined,错误。如下所示:

console.log(window.d);//undefined
console.log(d);//d is not defined

js代码由上而下执行,但在执行前,都有个词法分析过程。

js代码整体执行过程分:

  • 词法分析过程;
  • 执行过程。
词法分析
  1. 先分析参数
  2. 再分析变量声明
  3. 分析函数声明

一个函数能使用的局部变量,就从上面的3步分析而来。

具体步骤:

1、函数运行前的一瞬间,生成 Active Object(活动对象)

2、把声明的参数,形成AO的属性,值全是undefined;接收实参,形成AO相应的属性值

3、分析变量声明,如var age

如果AO上已经有age属性,则不做任何影响;
如果AO上没有age属性,则添加AO属性,值是undefined

4、分析函数声明,如function t(){}.

则把函数赋给AO.t属性

注:如果此前t属性已存在,则被无情覆盖

function t(age){
    alert(age);
}

t(5);//5
t();//undefined

词法分析过程:

AO{age:undefined}

运行过程:

t(5)->AO.age = 5,alert(AO.age);//5

t()->AO.age没得到赋值,还是undefined

function t2(age){
    var age = 99;
    alert(age);
}

t2(5);//99
分析过程

1、形成AO{}

2、分析形参,AO={age:undefined}

3、分析var age,发现AO已有age属性,不做任何影响

执行过程

AO.age = 99;

alert(AO.age);

function t3(greet){
    var greet = 'hello';
    alert(greet);
    function greet(){}
    alert(greet);
}

t3(null);//hello hello

function t(a){
    alert(a);
    var a = 2;
    alert(a);

    a = function(){
        alert(a);
        return 4;
    };

    alert(a());
}

t(22);
Arguments

Arguments,是个对象,很像数组的对象。

Arguments,是函数运行时,实参列表。

Arguments,收集所有的实参,即使没有与之对应的形参。

(function(a,b,c){
    console.log(typeof arguments);//object
    console.log(arguments);//["hello", "world", "!", " everyone"]
    console.log(arguments[0]);//hello
})("hello","world","!"," everyone");

形参与对应的Arguments元素,其实是相互映射,互相影响的。

(function(a,b,c){
    console.log(arguments);//["hello", "world", "!", " everyone"]
    console.log(arguments[1]);//world

    arguments[1] = "china";
    console.log(b);//china
})("hello","world","!"," everyone");

Arguments.callee属性代表“当前运行的函数”。适用:匿名函数,立即执行,完成递归。

console.log((function(n){
    //console.log(arguments.callee);
    if(n == 1){
        return n ;
    }
    else{
        return (n+arguments.callee(n-1));
    }
})(100));//5050

函数运行期内,关键的三个对象:

AO 本函数AO上没有其属性,则继续去外层函数上找,直到全局对象,叫做 作用域链

Arguments 每个函数有自己的callee,但不向外层接着找arguments的相关属性,即 不形成链

this 对象本身

this

js中函数调用this的4种方式:

作为普通函数来调用时,this的值指向window

准确的说,this为null,但被解释成window

在ECMASCRIPT5标准中,如果this为null,则解释成undefined。

console.log(window.x);//undefined
function t(){
    this.x = 210;
}
console.log(window.x);//210
作为对象的方法来调用

this指向方法的调用者,即该对象。

var obj = {x:99,y:11,t:function(){console.log(this.x)}};
obj.t();//99

var dog = {x:"wang"};
dog.t = obj.t;
dog.t();//wang

不管被调用函数,声明时属于方法还是函数。

show = function(){
   alert("show "+this.x);
}
dog.t = show;
dog.t();//show wang
作为构造函数调用时

js中没有类的概念,创建对象是用构造函数来完成,或直接用json格式{}来写对象。

new Dog发生了以下几个步骤:

  1. 系统创建空对象{},(空对象constructor属性指向Dog函数);
  2. 把函数的this指向该空对象,空对象变成{name:name,age:age}
  3. 执行该函数
  4. 返回该对象
function Dog(name,age){
    this.name = name;
    this.age = age;
    this.bark = function(){
        console.log(this.name + " bark");
    }
}

var dog = new Dog("Wang",2);
console.log(dog);//Dog {name: "Wang", age: 2}
dog.bark();//Wang bark

function Pig(){
    this.age = 5;
    return "abc";
}

var pig = new Pig();//new Pig返回的还是Pig对象,因为函数作为构造函数运行时,return后的语句就是无效的。

console.log(pig);//Pig {age: 5}
函数使用call、apply调用

函数.call(对象,参数1,参数2,...,参数n);

var human = {name:"yff",age:28}
function t(more){
    console.log(this.name+";"+(this.age+more));
}
t.call(human,2);//yff;30
b包
function t1(){
    var age = 20;
    function t2(){
        alert(age);
    }
    return t2;
}

var tmp = t1();

var age = 99;
tmp();//20 

在js中,t1执行过程中,又生成了t2,而从作用于上来说,t2能访问到age = 20,于是“age = 20”没有消失,而是于返回的t1函数形成了一个“环境包”,这个包属于t2,所以叫闭包。

返回的t2是有自己的生态环境。

在js中,age = 20这个变量,却被t2捕捉,即使t1执行完毕,通过t2,依然能访问该变量。返回的函数并非孤立的函数,甚至把其周围的变量环境形成了一个封闭的“环境包”,所以叫闭包。

一句话概括:函数的作用域取决于声明时,而不取决于调用时。

js对象的特点

在js中,对象不依赖于类而存在。

js的对象是一个“属性字典”,因此可以直接造对象。

var obj = {};//地球上最原始的蛋白质
var cell = {cell:1};//单细胞
var chicken = {head:1,leg:2,sing:function(){alert("sing sing")}};

{key:value}这种格式声明的对象,称为json格式的对象。

js的对象属性,是可以任意添加和删除的:

chicken.wine = 2;
delete chicken.wine;//删除chiecken的wine属性。

js中的对象,就是“一组属性与值的集合”,属性可以任意增减,方法和属性不必区分。

js的私有属性、封装

用闭包来完成js面向对象之私有属性。

function Girl(name,bf){
     var secret = bf;//私有属性,通过对象.secret是访问不了的。
     this.name = name;

     this.showLover = function(){//利用闭包封装,访问私有属性值。
         return secret;
     }

     this.moveLover = function(abf){//利用闭包封装,修改私有属性值。
         secret = abf;
     }
 }

 var girl = new Girl("黛玉","宝玉");
 alert(girl.name+"喜欢"+girl.showLover());

 girl.moveLover("方世玉");
 alert(girl.name+"移情别恋"+girl.showLover());
js的继承

js的继承,不是通过类的继承来实现的,而是通过“原型”概念来完成的。

function Tiger(){
     this.bark = function(){//利用闭包封装
         alert("我是百兽之王");
     }
 }

var heiTiger = new Tiger();

function Cat(){
    this.climb = function (){
        alert("我会爬树!");
    }
}

var bosiCat = new Cat();
bosiCat.climb();//我会爬树!

我们明确的对Tiger函数指定,用某个具体的Cat对象做Tiger的原型,并创建Tiger对象。

实现继承:

Tiger.prototype = new Cat();
var huananTiger = new Tiger();
huananTiger.climb();//我会爬树!

在这个过程,发生了什么?

  1. 构造空对象huananTiger{};
  2. huananTiger.bark = function(){};
  3. huananTiger.proto = Tiger.prototype(即Cat对象) 这是继承关键。

js中,每个对象都有一个proto指向原型对象,原型对象也是对象,也有proto

Tiger对象先在自身上寻找,没有climb()方法,去找原型,在原型Cat对象上找到climb()方法。

事件绑定
function t(){
alert('click you!');
}

<a href="#" onclick="t()">点击</a>

分析:

  1. DOM对象的句柄;
  2. 句柄上绑定的函数;
  3. 事件发生的那一瞬间,关于事件的各种信息,如时间、发生时鼠标在屏幕上的坐标、事件类型等等。

这些信息被打包成一个对象,便于获取。

把事件写在标签的属性里

把事件写在标签的属性里,如:<a href="#" onclick="t()" style="...">点击</a>

这是DOM 0级的标准(非常古老的标准)

好处:几乎所有的浏览器都支持

坏处:夹杂在html代码里,不简洁;事件写法,效率不高;不符合“行为、结构、样式相分离”

用事件属性来绑定事件函数
window.onload = function(){
    // 第一个好处:完成了行为的分离
    document.getElementById("test1").onclick = function(){
        alert('click you!');
    }

    // 第二个好处:便于操作当事对象,function是作为对象的on***属性出现的
    // 因此,函数里的操作对象,直接用this就能引用当事对象。
    document.getElementById("test2").onclick = function(){
        this.style.background = "red";
    }

    // 第三个好处:可以方便读取事件对象
    // 在事件触发时,系统自动把事件对象传递给事件函数,以其第一个参数来传
    document.getElementById("test3").onclick = function(ev){
        console.log(ev);
        var test4 = document.getElementById("test4");
        test4.style.left = ev.clientX;
        test4.style.top = ev.clientY;
    }
}
addEventListener

W3C中的标准:addEventListener

  1. 绑定在哪个事件上?click、change、load、focus、blur、mouseover...等等
  2. 绑定什么函数?自定义函数
  3. 什么方式监听执行事件函数?捕捉-true、冒泡-false,不填默认为false,建议填写。
var domobj = document.getElementById("xxx");
dombj.addEventListener("click",funciton(){
    alert(1);
},false);
dombj.addEventListener("click",funciton(){
    alert(2);
},false);
dombj.addEventListener("click",funciton(){
    alert(3);
},false);

注:

  1. 事件名一律不带on;
  2. 绑定事件函数中的this指绑定该事件的对象;
  3. 执行顺序按照绑定的顺序执行。

捕捉模型、冒泡模型

向内聚焦(外->内)->捕捉模型

向外扩散(内->外)->冒泡模型

<!doctype html>
<html>
    <head>
        <style script="text/css">
            #china{
                width:300px;
                height:300px;
                border:1px solid red;
                margin:auto;
            }
            #beijing{
                width:200px;
                height:200px;
                border:1px solid blue;
                margin:auto;
            }
            #haiding{
                width:100px;
                height:100px;
                border:1px solid green;
                margin:auto;
            }
        </style>
    </head>
    <body>
        <div id="china">
            <div id="beijing">
                <div id="haiding">

                </div>
            </div>
        </div>
    </body>
    <script>
        function $(id){
            return document.getElementById(id);
        }

        // 捕捉
        $("china").addEventListener("click",function(){alert("进入 China")},true);
        $("beijing").addEventListener("click",function(){alert("进入 beijing")},true);
        $("haiding").addEventListener("click",function(){alert("进入haiding")},true);

        //冒泡
        $("china").addEventListener("click",function(){alert("离开 China")},false);
        $("beijing").addEventListener("click",function(){alert("离开 beijing")},false);
        $("haiding").addEventListener("click",function(){alert("离开 haiding")},false);

        // 按照该顺序,正常顺序执行;
        //若将冒泡与捕捉对调,不按照顺序正常执行
    </script>
</html>

阻止事件

W3C标准:

  • 阻止事件传播:event.stopPropagation();
  • 阻止事件效果:eventPreventDefault();
    // 修改捕捉里beijing绑定的事件
    $("beijing").addEventListener("click",function(event){
    alert("进入 beijing");
    event.stopPropagation(); //不再执行下面绑定的事件
    },true);

IE8及以下:

  • event.cancelBundle();
  • event.returnValue=false;

解除绑定

  • obj.removeEventListener("事件",监听函数名,true/false);

IE8及以下:

  • obj.detachEvent("on事件",监听函数名,true/false)
静态属性、方法

Javascript语言的面向对象特征很弱,其他面向对象语言在创建类时只要使用关键字static即可指定类为静态类,Javascript没有提供static这样的关键字,要让Javascript也具有“静态”特性只有靠一些技巧了。

代码中列举了两种静态方法/属性的实现方式:

  1. 静态类的静态方法和属性;
  2. 非静态类的静态方法和属性。
类、方法、属性都为静态类型
/****************************************
* 方法一
* 类、方法、属性都为静态类型
* 不能创建实例
*****************************************/
var Time = {
    today: '2009-3-8', 
    weather: 'rain', 
    show: function() {
        alert('Today is ' + this.today); 
    }
}; 

//静态对象可直接使用,无需创建实例
alert('It is ' + Time.weather + ' today.'); 
Time.show(); 

//下面的代码会出错,因为静态类不能创建实例
//var t = new Time();
//t.show();
普通对象,同时拥有静态和非静态属性、方法
/****************************************
* 方法二
* 普通对象,同时拥有静态和非静态属性、方法
* 可以用实例化
* 注意:
*   1.静态方法/属性使用类名访问
*   2.非静态方法/属性使用实例名访问
*****************************************/
function Person(name) {
    //非静态属性
    this.name = name; 
    //非静态方法
    this.show = function() {
        alert(‘My name is ‘ + this.name + ‘.’); 
    }
}

//添加静态属性,人都是一张嘴
Person.mouth = 1; 
//添加静态方法,哇哇大哭
Person.cry = function() {
    alert(‘Wa wa wa …’); 
};

//使用prototype关键字添加非静态属性,每个人的牙可能不一样多
Person.prototype.teeth = 32; 

//非静态方法必须通过类的实例来访问
var me = new Person('Zhangsan'); 
//使用非静态方法、属性
me.show(); 
alert('I have ' + me.teeth + ' teeth.'); 
//使用静态方法、属性
Person.cry(); 
alert('I have ' + Person.mouth + ' mouth.');
对象、实例上添加方法、属性
  • 利用prototype

prototype 属性使您有能力向对象添加属性和方法。

  1. 当构建一个属性,所有的对象将被设置属性,它是默认值。
  2. 在构建一个方法时,所有的对象都可以使用该方法。
var BaseClass = function() {};  
//BaseClass的每个对象上的原型都有method1方法。
BaseClass.prototype.method1 = function(){  
      alert(' This is a instance method ');  
}  
var instance1 = new BaseClass();
//method1这个是BaseClass对象上的方法,所有BaseClass的实例都有该方法
instance1.method1();
  • 实例上直接定义
var BaseClass = function() {};  
var instance1 = new BaseClass();  
instance1.method1 = function(){  
    alert(' This is a instance method too ');  
}
//只有instance1这个实例对象上有method1方法。
instance1.method1();
  • 构造函数里定义
//BaseClass的每个对象上都有method1方法。
var BaseClass = function() {
    this.method1 = function(){  
        alert(' This is a instance method too ');  
    }
}
var instance1 = new BaseClass();
instance1.method1();
js里的方法介绍
apply、call
  • apply:方法能劫持另外一个对象的方法,继承另外一个对象的属性.

Function.apply(obj,args)方法能接收两个参数

obj:这个对象将代替Function类里this对象

args:这个是数组,它将作为参数传给Function(args-->arguments)

<script type="text/javascript">
    /*定义一个人类*/
    function Person(name,age) {
        this.name=name; this.age=age;
    }

     /*定义一个学生类*/
    function Student(name,age,grade) {
        Person.apply(this,arguments); 
        this.grade=grade;
    }   

    //创建一个学生类   
    var student=new Student("qian",21,"一年级");

    //测试
    alert("name:"+student.name+"\n"+"age:"+student.age+"\n"+"grade:"+student.grade);
    //name:qian age:21 grade:一年级   

    //学生类里面没有给name和age属性赋值,为什么又存在这两个属性的值呢?
    //这个就是apply的神奇之处.

    //分析: Person.apply(this,arguments);  
    //this:在创建对象在这个时候代表的是student  
    //arguments:是一个数组,也就是["qian","21","一年级"];  
    //也就是通俗一点讲就是:用student去执行Person这个类里面的内容
    //在Person这个类里面存在this.name等之类的语句,这样就将属性创建到了student对象里面  
</script> 
  • call:和apply的意思一样,只不过是参数列表不一样.

Function.call(obj,[param1[,param2[,…[,paramN]]]])

obj:这个对象将代替Function类里this对象

params:这个是一个参数列表

//call例子:将apply里的例子里,apply换成call,就可以了。
  • 什么情况下用apply,什么情况下用call

在给对象参数的情况下,如果参数的形式是数组的时候,比如apply示例里面传递了参数arguments,这个参数是数组类型,并且在调用Person的时候参数的列表是对应一致的(也就是Person和Student的参数列表前两位是一致的) 就可以采用 apply , 如果我的Person的参数列表是这样的(age,name),而Student的参数列表是(name,age,grade),这样就可以用call来实现了,也就是直接指定参数列表对应值的位置(Person.call(this,age,name,grade));

  • apply小技巧(参数列表改成数组传值)

Math.max(param1,param2,param3…) ->var max=Math.max.apply(null,[param1,param2,param3…])

Math.min(param1,param2,param3…) ->var max=Math.min.apply(null,[param1,param2,param3…])

array.push(param1,param2,param3…) ->Array.prototype.push.apply(array,[param1,param2,param3…]);

setTimeout

settimeout()方法要调用带参数的函数有两种方法:

  • setTimeout(匿名函数,秒数)
var a=3;
setTimeout(function(){console.log(a)},1000);//1秒后执行,输出3
  • setTimeout(字符串,秒数)
function test(a){
    console.log(a);
}
//这里的参数只能是字符串形式的,而不能传递一个对象 
setTimeout("test("+3+")",1000);//1秒后执行,输出3

var b = "abc5";
// 注意双引号内有单引号
setTimeout("test('"+b+"')",2000);//2秒后执行,输出abc5,
点击查看更多内容
7人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消