前端点滴(JS基础)(一)----倾尽所有

前端点滴(JS基础)(一)----倾尽所有

js基础

一、js的组成

ECMAScript(ES) : 负责翻译,为js的核心,解释器。

DOM (Document Object Model 文档对象模型):赋予js操作HTML的能力,document。

BOM (Browser Object Modal 浏览器对象模型):赋予js操作浏览器的能力,window,不兼容不建议使用。

二、js放置位置

js放置的位置可以在head中,body中,body外,js文件中。
但是需要考虑js阻塞问题:
除了一些在页面执行前必须加载的js以外,大多数js应该放置在body标签内的尾部。这主要是因为js会阻塞其后内容的加载和呈现,导致白屏。此外,js的执行过程可能作用于dom tree内的元素,如果放置在前面会报错。
为了避免js阻塞其后内容,可以根据具体需求使用参数async或defer,使JS异步加载。
async,加载和渲染后续元素的过程将和js文件的加载与执行并行进行,async的js元素下载完后立刻执行,不同js的先后执行顺序很可能与页面中的出现次序不同。
defer,加载后续文档元素的过程将和js的加载并行进行,但js的执行要等到所有元素解析完成之后,DOMContentLoaded事件触发之前完成。

三、js输出方式

1、document.write(输出的内容); //这种输出的内容会显示在浏览器页面上
2、alert(输出的内容); //这种方法输出的内容会以提示框的形式显示
3、console.log(输出的内容); //输出的内容会显示在浏览器的控制台。(推荐使用)。

四、js语句与表达式

JavaScript程序的执行单位为行(line),也就是一行一行地执行。一般情况下,每一行就是一个语句。
JS语言中,一条语句结束,可以不用写分号,直接用回车即可。但是建议每条语句后面都加分号。

五、js变量

1. 什么是变量

  • 什么是变量
    变量是计算机内存中存储数据的标识符,根据变量名称可以获取到内存中存储的数据
  • 为什么要使用变量
    使用变量可以方便的获取或者修改内存中的数据
  • 变量分为
    全局变量,局部变量(函数中)
    注意:修改全局变量可以不用再次声明
    例如:
var a = 1;
a = 2;// var a = 2;
console.log(a);   //=> 2
  • 变量的生命周期
    JavaScript 变量的生命期从它们被声明的时间开始。
    局部变量会在函数运行以后被删除。
    全局变量会在页面关闭后被删除。

2. 如何使用变量

(1)变量的声明
var、let(es6)、const

var:最常用的声明变量关键字。 定义的变量的时候,若没有初始化,不报错,会输出undefined。其值之后可以修改。
var可以用来声明全局变量,也可以声明局部变量,依据它们声明的位置:

  • 全局变量:在函数外定义的变量;(注意:若没有使用关键字声明的变量,默认为全局变量。)作用域是整个代码文件。
  • 局部变量:在函数内定义的变量。作用域是当前的函数内部。

let:块级作用域 。在块级{}里面用let定义的变量,离开当前的块{}之后,就不能使用(有点像局部变量,但作用域不一样)。
注意:{…}一对花括弧就就是一个特定的代码块,包括直接的{},流程语句的{},函数的{},循环的{}…。函数声明时本身就带有{},也是属于一个代码块。

const:用于声明常量。注意:定义的变量的时候,必须同时初始化,且其值之后不可以修改。

var、let 声明变量的区别

var 声明变量,具有全局作用域

四大特性:
1.全局作用域
2.同级作用域下,可以存在多个var 声明的变量
3.会发生变量提升
4.设置i仅为循环数组,但循环后,残留一个变量i。

let 块级变量,声明的对象具有块级作用域

四大特性:
1.块级作用域:{}下起作用
2.同级作用域下,只能存在一个(let声明的变量不能重复声明)
3.不能变量提升
4.let 在循环中经常被使用(重点),i变量只在for()内有效,不污染其他区域

const 常量声明

特性:常量的作用域,是常量所在的块。(块可以理解为大括号)
在这里插入图片描述

(2)变量的赋值

这里的值可以是null,string类型的值,number类型的值,boolean类型的值,object(Function、Array、Object)…
也可以不赋值,当输出后会显示undefined
在这里插入图片描述

3. 变量在内存中的形式

let a1 = 0; // 栈内存
let a2 = "this is string" // 栈内存
let a3 = null; // 栈内存
let b = { x: 10 }; // 变量b存在于栈中,{ x: 10 }作为对象存在于堆中
let c = [1, 2, 3]; // 变量c存在于栈中,[1, 2, 3]作为对象存在于堆中

在这里插入图片描述
当我们要访问堆内存中的引用数据类型时

  1. 从栈中获取该对象的地址引用
  2. 再从堆内存中取得我们需要的数据

详细请参考:https://www.jianshu.com/p/80bb5a01857a

4. 变量相关问题

(1)只声明变量没有赋初值

在这里插入图片描述

(2)重新声明变量

在这里插入图片描述

(3)变量提升

在这里插入图片描述
在同一个作用域{}中,都会发生变量提升,比如函数中。
在这里插入图片描述

六、js数据类型

ECMAScript变量可能包含两种不同数据类型的值:基本类型值和引用类型值。基本类型值指的是简单的数据段,而引用类型值指那些可能由多个值构成的对象。

1. 原始类型和引用类型

基本类型和引用类型这里可称为: 原始类型(值类型)和对象类型(引用数据类型)

原始类型、值类型、基本类型:
null: 只包含一个值: null
undefined: 只包含一个值:undefined
boolean: 包含两个值: true和false
number:整数或浮点数,还有一些特殊值(-Infinity、+Infinity、NaN)
string: 一串标识文本值的字符序列
symbol: 一种实例是唯一且不可改变的数据类型(es6新增)

对象类型、引用数据类型:
Object: 除了常用的Object, Array,Function等都属于特殊的对象

继续细分则是:
5种基本类型:null,undefined,boolean,number,string
1种复杂类型:object
5种引用数据类型:Array,Object,Function,Date,RegExp
3种基本包装类型:boolean,number,string
2种单体内置对象:global(全局属性和函数),math

它们可以使用typeof、instanceof 来查看变量类型,一个变量应只存一个类型的数据。

<script type="text/javascript">
console.log(typeof 1)		//=> number
console.log(typeof "1")		//=> string		
console.log(typeof true)	//=> boolean
console.log(typeof {})		//=> object
console.log(typeof [])		//=> object
console.log(typeof function(){})		//=> function
console.log(typeof Symbol(1))		//=> symbol
var a;
console.log(typeof a);		//=> undefined
console.log(typeof undefined)		//=> undefined
console.log(typeof null)		//=> object
</script>

为了彻底分清数据类型,封装一个判断:

var a = 123;
var b = 3.14;
var c = 'hello';
var d = true;
var e;
var f = null;
var g = [];
var h = {};
function m(){}

function panduan(x){
        if(typeof(x) != 'object')
        {
            return typeof(x);
        }
        else
        {
            if(x instanceof Array)
            {
                return 'array';
            }else if(x instanceof Object){
                return 'object';
            }else{
                return 'null';
            }
        }
    }
console.log(panduan(a));  //=> number
console.log(panduan(b));  //=> number
console.log(panduan(c));  //=> string
console.log(panduan(d));  //=> boolean
console.log(panduan(e));  //=> undefined
console.log(panduan(f));  //=> null
console.log(panduan(g));  //=> array
console.log(panduan(h));  //=> object
console.log(panduan(m));  //=> function

引出问题:
1、console.log(null instanceof Object); //=> false
2、console.log(typeof null); //=> object (使用typeof判断得来)
3、console.log(panduan(f)); //=> null (使用instanceof判断得来)

为什么会矛盾?????
看似二者有些矛盾,但null返回object这个其实是最初JavaScript的实现的一个错误,然后被ECMAScript沿用了,成为了现在的标准,不过我们把null可以理解为尚未存在的对象的占位符,这样就不矛盾了 。对于Null类型的值(只有null),规范就是定义返回"object"这个字符串。但是本质上Null和Object不是一个数据类型,null值并不是以Object为原型创建出来的。所以null instanceof Object是false。但从这里也可以看到,null确实是javascript中用来表示空引用的一个特殊值。使得它不是instanceof Ojbect,而typeof null是“object”。在语义上也是可以理解的。

个人理解(道家学说):
无物不属于万物,却似于万物。

详细可以参考:
https://blog.csdn.net/weixin_42049636/article/details/81565461
https://www.jianshu.com/p/659cbcdcebfd

2. 原始类型和引用类型的区分

引用类型指对象,指的是狭义上的对象。数组不应该算引用类型。
区分是否是引用类型,关键看内存图。
对象在内存的栈区只保存它的地址,在堆区保存它的实际内容,这种类型的变量就是引用类型。
在这里插入图片描述
引用类型的一些应用:引用类型可以作为函数的参数

例如:
参数是一个数组

var arr = [1,2,3]
function change(arr){
arr.push(4);
console.log(arr);
}
change(arr);    //=>[1, 2, 3, 4]

参数是一个对象

var obj = {name:'chenjianer',age:20}
function change(o){
o.name = 'yaodao'
console.log(o);
}
change(obj);      //=>{age: 20, name: "yaodao"}

参数是一个函数 function(称为回调函数),后期经常使用

var callback = function(a){
console.log(a);
};
function change(callback){
var a = 2;
callback (a);
}
change(callback);

3. undefined的产生

js中undefined的几种情况:(一变一对象两函数)
1、变量声明且没有赋值;(或者发生变量提升)
2、获取对象中不存在的属性时;
3、函数需要实参,但是调用时没有传值,形参是undefined;
4、函数调用没有返回值或者return后没有数据,接收函数返回的变量是undefined。

4. null的产生

1、作为函数的参数,表示该函数的参数不是对象
2、作为对象原型链的终点

5. undefined与null之间的区别

1、null和undefined的类型不同

console.log(typeof(undefined)); //undefined
console.log(typeof(null)); //object

null是一个具有有效不存在值的对象,并且它是不可变的,而undefined的对象类型是本身未定义的

此外任何具有null值的算术运算都将产生整数值,而任何带有undefined的算术运算都会导致变量值变为NaN

2、转换为原始类型的方式不同
null和undefined之间的主要区别在于它们被转换为原始类型的方式。在null上执行算术转换时,确定的值为0可以使用以下代码片段验证此转换。

var v1= 5+ null;
console.log(v1)

输出结果为5

但是undefined不执行任何此类转换,如果将undefined添加到数字中得出的结果将为NaN

var v2= 5+ undefined;
console.log(v2)

输出结果为NaN

七、js数据类型的转换

1. 转换成字符串(String)类型

  • 使用字符串方法toString([进制])转换,但是null和undefined无法转换。
  • 使用顶层函数String()可以将任何数据类型转换成字符串,包括null和undefined。
  • 在其他数据类型和字符串类型进行连接 (+操作) 操作时,会自动对其他数据类型使用String()强制转换成字符串,然后在相加(连)(隐性转换)

将其他数据类型的值转换成字符串,可以使用toString(),也可以使用String()。区别在于String可以转换任何类型的值为字符串,toString()不能转换undefined和null。区别二是语法不同。

使用语法:
使用toString() : 待转换的变量.toString();
使用String() : String(待转换的变量);

(1)使用toString()转换

在这里插入图片描述

(2)使用String()转换

在这里插入图片描述

(3)隐性转换

隐式转换:在其他数据类型和字符串类型进行连接(+操作)操作时或者比较(==操作),会自动对其他数据类型使用String()强制转换成字符串,然后在相加(连)。除此之外,还有(-操作),但是此操作会自动对其他数据类型使用parseFloat()强制转换成浮点型,然后再数学相减。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
上述的[object Object] 第一个object表示是何种数据类型,第二个Object表示是那种类型的对象

2. 转换成数值(Num)类型

(1)parseInt() 转换成整型

parseInt() 方法首先查看开头位置的字符,判断它是否是个有效数字;如果不是,该方法将返回 NaN(not a number),不再继续执行其他操作。但如果该字符是有效数字,该方法将查看下一位置的字符,进行同样的测试。这一过程将持续到发现非有效数字的字符为止,此时 parseInt() 将把该字符之前的字符串转换成数字。

例如,如果要把字符串 “12345red” 转换成整数,那么 parseInt() 将返回 12345,因为当它检查到字符 r 时,就会停止检测过程。

注意的是:字符串中包含的数字字面量会被正确转换为数字,比如 “0xA” 会被正确转换为数字10。原因parseInt 按照进制数(基数)解析,0x表示16进制,A在16进制下表示10,parseInt 的正确语法是 parseInt(string, radix(进制数))。不过,字符串 “22.5” 将被转换成22,因为对于整数来说,小数点是无效字符。
在这里插入图片描述
在这里插入图片描述

(2)parseFloat() 转换成浮点型

道理和转换成整型道理一样,只不过浮点型允许有一个小数点出现。
在这里插入图片描述

(3)显性转换(Number() 强制转换)

Number() 函数的强制类型转换与 parseInt() 和 parseFloat() 方法的处理方式相似,只是它转换的是整个值,而不是部分值。

用 Number() 进行强制类型转换,“1.2.3” 将返回 NaN,因为整个字符串值不能转换成数字。如果字符串值能被完整地转换,Number() 将判断是调用 parseInt() 方法还是 parseFloat() 方法。
在这里插入图片描述

3. 转换成布尔(boolean)类型

显示的转换是使用Boolean()函数,对需要转换的内容进行转换。
以下内容在转换成布尔值时会被转换成false:

  • 数值型的 0
  • 数值型的 0.0
  • 布尔型的 false
  • 空字符串 “”
  • 非数字 NaN
  • undefined
  • null

其他所有值都会转换成true,包括 “0”、空数组 [] 和空对象 {} 。

八、js运算符

1. 赋值运算符

给定 x=10 和 y=5

运算符例子等同于运算结果
=x=yx=yx=5
+=x+=yx=x+yx=15
-=x-=yx=x-yx=5
*=x*=yx=x*yx=50
/=x/=yx=x/yx=2
%=x%=yx=x%yx=0

2. 比较运算符

在这里插入图片描述
在这里插入图片描述

3. 算数运算符

+加-减*乘%除(取余)
在这里插入图片描述

4. 逻辑运算符

与、或、非

与(&& )关系,先看左边,左边为true,直接等于右边;左边为false,直接等于左边。
或(|| )关系,先看左边,左边为true,则直接等于左边;左边为false,直接等于右边。
非(!)

var a = 1, b = 2, c = 0, d = false;
var e = a||b; // e = a;
console.log(a||b); // 1
console.log(a||c); // 1
console.log(c||a); // 1
console.log(c||d); // false
// || 关系,先看左边,左边为true,则直接等于左边;左边为false,直接等于右边

console.log(a&&b); // 2
console.log(a&&c); // 0
console.log(c&&a); // 0
console.log(c&&d); // 0
// && 关系,先看左边,左边为true,直接等于右边;左边为false,直接等于左边。
console.log(10||20); // 10
/*var x = 10&&20;
console.log(x||30); // 20*/
//console.log(0&&5); // 0

5. 字符串运算符

PHP中用 点(.) 连接两个字符串。
JS中用 加号(+) 连接两个字符串。
如果使用+的是两个数字,则表示加法运算;如果使用+的有一方是字符串,则表示字符串连接。

console.log(2+3);    // 5,表示加法运算,因为参与运算的两个值都是数值型
console.log(2+'hello');    // 2hello,表示字符串相连
console.log('hello ' + 'world');    //hello world

6. 条件(三元)运算符

元,表示参与运算的参数个数。三元意思就是参与运算的有三个值。

var a = (b>c) ? x : y;

上述代码整体意思是给a赋值,值可能是x,也可能是y。关键取决于b是否大于c。如果b>c,则将x赋值给a;如果b不大于c,则将y赋值给a。

7. 一元运算符

var a = 2, b = 3;
var c = a++;    // var c = a; a = a+1
var d = ++b;    // b = b+1; var d=b;
console.log(a, b , c, d);    // a=3, b=4, c=2, d=4

九、 js数组

1. 数组的声明

在JS中,数组也是一种特殊的对象。属于 typeof [1,2,3] == object,但实际上[1,2,3] instanceof Object 为 false。因为数组属于 Array 类型。

三种声明方式:
① var arr = [‘apple’, ‘pear’]; // 推荐使用
② var arr = new Array(‘apple’, ‘pear’); // 一般推荐
③ var arr = new Array(3); //表示数组中有三个单元

2. 获取数组的元素

使用“数组[下标]”可以获取到数组中的值。JS中数组的下标一定是数字类型的。
在这里插入图片描述

3. 判断下标是否存在

使用 in 来判断下标是否存在。
语法: 下标 in 数组 如果存在返回true,不存在返回false。
在这里插入图片描述

4. 清空数组

清空数组的两种方式:

  1. 使数组 = [];
  2. 数组.length = 0;

在这里插入图片描述

十、 js对象

1. 创建对象

JS中要得到(定义)一个对象有很多种方式:

  1. 直接量语法得到(定义)对象
  2. 构造函数方式得到(定义)对象
  3. 原型对象方式得到(定义)对象
  4. 混合方式得到(定义)对象(推荐使用)
(1)直接量语法得到对象

语法:

var obj = {}; //空对象
var obj = {成员名:, 成员名:}; 
var obj = {
	成员名:,
	成员名:};
(2)构造函数得到对象

这种方式的弊端是,会为每一个对象开辟一个内存,比较占空间,好办法是将p1和p2相同部分放到原型对象上
语法:

function people(){
            this.name = 'chenjianer';
            this.age = 20;
            this.say = function(){
                console.log('帅哥一枚!');
            }
        }   
var p1 = new people();
console.log(p1);

在这里插入图片描述

(3)原型对象方式得到对象

这种方式的弊端是,如果原型对象中有一个引用类型(数组,对象…)的值,则修改其中一个实例对象,另外一个也会修改

function person(){

}
//原型对象
person.prototype.name = 'chenjianer';
person.prototype.age = 20;
person.prototype.sanwei = ['100cm','90cm']
person.prototype.say = function(){
      console.log(123);
}
var p1 =new person();
console.log(p1);

在这里插入图片描述

(4)混合方式得到对象(推荐使用)

实际开发中,推荐使用这种方式,减少内存的消耗

function person(n,a){
            this.name = n;
            this.age = a;
            this.sanwei = ['100cm','90cm','110cm'];
        }
        //原型对象(函数类型)
        person.prototype.say = function(){
            console.log(123);
        } 
        person.prototype.cook = function(){
            console.log('能煮一手好饭菜');
        }
        //实例化得到对象
        var p1 = new person('chenjianer',20);
        console.log(p1);

在这里插入图片描述

2. 获取对象的成员

点语法用于对象调用里面的成员
在这里插入图片描述
对象内部的 this 表示对象本身
在这里插入图片描述

3. 判断对象中的成员

使用 in 来判断属性是否存在与对象中。
语法:对象成员 in 对象,存在返回true,不存在返回false。

4. 添加对象的成员

使用点语法添加对象成员。
在这里插入图片描述

5. 删除对象的成员

使用delete关键字来删除对象的成员。
在这里插入图片描述

6. 修改对象成员

还是使用点语法修改对象成员,先找到对象成员再重新赋值(此处省略)。

十一、 js流程控制

1. 顺序结构

js默认的流程结构。按照书写顺序从上至下执行每一条语句。需要注意的是js不存在等待的说法。

2. 分支结构

(1)条件 (if…else…) 语句
if 语句

语法:

if (condition){
    当条件为 true 时执行的代码
}
if…else… 语句

语法:

if (condition){
    当条件为 true 时执行的代码
}else{
    当条件不为 true 时执行的代码
}
if…else if…else 语句
if (condition1){
    当条件 1true 时执行的代码
}else if (condition2){
    当条件 2true 时执行的代码
}else{
  当条件 1 和 条件 2 都不为 true 时执行的代码
}
(2)switch语句

工作原理:首先设置表达式 n(通常是一个变量)。随后表达式的值会与结构中的每个 case 的值做比较(比较规则是 n === 条件,不仅值要相等,类型也要相同)。如果存在匹配,则与该 case 关联的代码块会被执行。请使用 break 来阻止代码自动地向下一个 case 运行。
语法:

var n = ...   // switch参数
switch(n){
    case 条件1:
        执行代码块 1
        break;
    case 条件2:
        执行代码块 2
        break;
    default:case 条件1case 条件2 不同时执行的代码
}

3. 循环结构

(1)for 循环
for 循坏

循环(遍历)对象:Num数组,String数组,Object数组
语法:

for (语句 1; 语句 2; 语句 3){
    被执行的代码块
}

语句 1: (代码块)开始前执行
语句 2: 定义运行循环(代码块)的条件
语句 3: 在循环(代码块)已被执行之后执行

形式一:

for (var i=0; i<5; i++){
      被执行的代码块
}

形式二:

var i=0,len=arr.length;
for (; i<len; i++){ 
    被执行的代码块
}

形式三:

var i=0,len=arr.length;
for (; i<len; ){ 
    被执行的代码块
    i++;           /*在这++*/
}

其中 i 的声明使用的声明方法请看上述的变量声明

for in 循环

语法:

for (var i in arr){     
    被执行的代码块
}

实例:

var arr = ['关羽', '张飞', '赵云', '马超', '黄忠'];
    for(var b in arr){
        //b 表示数组的下标
        //arr[b] 表示数组中的每个值
        console.log(b, arr[b]);
    }
(2)while 循环
while 循环

while 循环会在指定条件为真时循环执行代码块。
语法:

while (条件){
    需要执行的代码;
    条件自增
}
do while循环

该循环会在检查条件是否为真之前执行一次代码块,然后如果条件为真的话,就会重复这个循环。

do{
    需要执行的代码
    条件自增
}
while (条件);

区别说明:

/*while循环*/
var a = 1;
    while(a < 1){
        console.log(a);       //=> 没有输出
        a++;
    }

/*do while 循环*/
var a = 1;
    do{
        console.log(a);     //=> 输出为 1 
        a++
    }while(a < 1);

说明 while 是判断后执行,do while 是执行后判断 。

(3)break 和 continue 语句

break 语句用于跳出循环。(终止)
continue 用于跳过循环中的一个迭代。

break 语句

使用break语法的 for 循环

var i=0;
    for( ; ; ){
        if(i == 10){
            //终止循环
            break;
        }
        需要执行的代码;
        i++;
    }

简写:

var i=0;
    for( ; ; ){
        if(i == 10)  break;   //终止循环
        需要执行的代码;
        i++;
    }
continue 语句
var i=0;
    for( ; ; ){
        if(i == 2){
            // 跳过当前迭代
            continue;
        }
        需要执行的代码;
        i++;
    }

简写:

var i=0;
    for( ; ; ){
        if(i == 2)  continue; 	 	// 跳过当前迭代
        需要执行的代码;
        i++;
    }

十二、js函数基础

1. 函数定义与调用

定义语法:

function 函数名(参数列表) {
	//函数体
	//return xxx;
}

实例:

/**************** 使用function声明函数,并调用 *******************/

//定义函数,参数为一个大于0 的数字n,要求返回1+2+3+....+n 的和
function sum(n){
    //定义一个和
    var s = 0; //默认是0
    for(var i=1; i<=n; i++){
        s = s + i; // s += i;
    }
    //循环结束之后,就得到一个和
    return s;
}
//调用函数
console.log(sum(100));

这种方式定义的函数可以先调用,后定义,也就是函数预加载。

2. 函数表达式

将其看做是一个值,并赋值给一个变量。

var a = sum;
console.log(a(100));   //得到的结果相同

既然函数可以看做是变量,那么就可以像定义变量一样来定义函数,这就是函数表达式的形式:

var a = function sum(x){
	console.log('总数为'+x);
}
a(5050);   // 调用函数

3. 函数的预加载

什么是函数的预加载?
就是先调用,后声明。
函数预加载指的是哪种方式定义的函数呢?
在这里插入图片描述
原因:变量的提升
函数预加载指定是在同一个script代码段中,由“function xxx(){}”这种方式定义的函数,可以先调用函数,再声明函数。
注意不要在非函数的代码块中声明函数。

4. 立即调用模式

(1)立即执行函数是什么

立即执行函数就是

  1. 声明一个匿名函数
  2. 马上调用这个匿名函数
(2)为什么使用匿名函数

为的就是营造私密环境,使得变量不被污染,变量不丢失

(3)匿名函数的作用

只有一个作用:
创建一个独立的作用域。
这个作用域里面的变量,外面访问不到(即避免「变量污染」)。

以一个著名的面试题为例:

var liList = ul.getElementsByTagName('li')
for(var i=0; i<6; i++){
  liList[i].onclick = function(){
    alert(i) // 为什么 alert 出来的总是 6,而不是 0、1、2、3、4、5
  }
}

为什么 alert 的总是 6 呢,因为 i 是贯穿整个作用域的,而不是给每个 li 分配了一个 i,或者可以这么理解:就是for循环瞬间执行完,函数都还没开始执行 i 就已经丢掉了,因此需要== 用()将 i保存起来== 这是关键,所以如下:

(4)立即调用与回调函数

那么怎么解决这个问题呢?用立即执行函数给每个 li 创造一个独立作用域即可(当然还有其他办法):
方法一:(立即调用模式)

var liList = ul.getElementsByTagName('li')
for(var i=0; i<6; i++){
  (function(j){
    liList[j].onclick = function(){
      alert(j) // 0、1、2、3、4、5
    }
  })(i)
}

方法二:(回调函数)

var liList = ul.getElementsByTagName('li');
function callback(i){
    liList[i].onclick = function(){
        alert(i);
    };
};
function click(callback){
    for(var i=0; i<6; i++){
        callback(i);
    };
};
click(callback);

在立即执行函数执行的时候,i 的值被赋值给 j,此后 j的值一直不变。

i 的值从 0 变化到 5,对应 6 个立即执行函数,这 6 个立即执行函数里面的 j 「分别」是 0、1、2、3、4、5,起到了保存作用。

5. 函数中的this指向

<script type="text/javascript">
(function test(){
	console.log(this);    //=> this指向全局对象
})();

function test(){
	this.name = 'chenjianer'    //=> this指向本身实例化对象
};
var test = new test();   // 实例化得到对象
console.log(test.name);
</script>

在这里插入图片描述

6. 函数的参数

形参:定义函数时,约定的参数
实参:调用函数时,传递给函数的实际值(实际值包括普通值,引用类型中的值)。
JS函数,参数传递非常灵活,定义的形参和实际传入的实参个数可以不一样。
在这里插入图片描述
ES5中,函数的参数不可以用默认值。ES6中,函数的参数可以有默认值的。目前IE11只支持部分ES6的内容。
那么在ES5中,如何实现形参有默认值的写法?
在这里插入图片描述

7. 函数作用域

1. 作用域分类

作用域指的是变量起作用的范围。
分为全局作用域和局部作用域。其中局部作用域也叫做函数作用域。

2. 作用域规则
(1)规则一:函数可以使用函数以外的变量

在这里插入图片描述

(2)规则二:函数内部,优先使用函数内部的变量

在这里插入图片描述
函数内部也会发生变量提升:
在这里插入图片描述

(3)规则三:函数内部没有用var声明的变量,也是全局变量

在这里插入图片描述

3. 作用域链

在内部函数中查找变量的时候,优先从函数内部自身查找,如果没有查到,则向外层查找,如果外层还没有,则继续向上一层查找,一直查询到全局作用域。这种链式的查找方式就是作用域链。
注意:在函数中的var 声明的变量称为局部变量,只能在函数内部访问,全局下是不能被访问的。详细请看:函数内的变量说明
在这里插入图片描述

8. 函数内的变量说明

(1)局部 JavaScript 变量

在 JavaScript 函数内部声明的变量(使用 var)是局部变量,所以只能在函数内部访问它。(该变量的作用域是局部的)。
您可以在不同的函数中使用名称相同的局部变量,因为只有声明过该变量的函数才能识别出该变量。
只要函数运行完毕,本地变量就会被删除。

(2)全局 JavaScript 变量

在函数外声明的变量是全局变量,网页上的所有脚本和函数都能访问它。
自调用模式下的全局变量声明,网页上的所有脚本和函数都能访问它。

(3)JavaScript 变量的生存期

JavaScript 变量的生命期从它们被声明的时间开始。
局部变量会在函数运行以后被删除。
全局变量会在页面关闭后被删除。

实例:

/*自调用匿名函数---立即调用模式下*/
var name = 'zhangsan';
(function(){
	console.log(name)//这里因为变量提升,name === undefined,所以结果是undefined
	var name = 'lisi'
	console.log(name)//这里执行的是 name = lisi ,所以自然就是lisi了
})();
console.log(name)//函数的作用域,在全局中无法访问,这里结果是张三

var name = 'zhangsan';
(function(){
	console.log(name)//函数内部没有name 这个变量,所以像全局查找,全局有一个name,那么结果就是zhangsan
	name = 'lisi'
	console.log(name)//同理,这里结果是lisi,因为name被赋值成lisi
})()
console.log(name)//lisi
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值