JavaScript

JS的变量与保留字

一、变量

  • 在js中使用var关键字,进行变量的声明,(var是英语“variant”变量的缩写)注意 分号作为一句代码的结束符
  • 声明变量时可以不用var. 如果不用var那么它是全局变量
  • 命名方式:Pascal标记法命名的变量前附加一个小写字母(或小写字母序列),说明该变量的类型。例如,i 表示整数,s 表示字符串,如下所示“ Var iMySalary = 1000000, sMyInfo = "hello";
<script type="text/javascript">
//var还可以一次声明多个变量,变量之间用,逗号隔开。
var x,y;
var x = 5;//声明变量x并将5赋值给x

var x; //声明变量x
x = 5; //给变量x赋值

</script>
View Code
var sleep = function(time) {
        var startTime = new Date().getTime() + parseInt(time, 10);
        while(new Date().getTime() < startTime) {}
};

//调用
sleep(1000);
sleep

二、保留字

abstract、boolean、byte、char、class、const、debugger、double、enum、export、extends、final、float、goto implements、import、int、interface、long、native、package、private、protected、public、short、static、super、synchronized、throws、transient、volatile、

三、语言类型

  • 静态类型语言

    一种在编译期间就确定数据类型的语言。大多数静态类型语言是通过要求在使用任一变量之前声明其数据类型来保证这一点的。Java 和 C 是静态类型语言。
  • 动态类型语言

    一种在运行期间才去确定数据类型的语言,与静态类型相反。VBScript 和 Python 是动态类型的,因为它们确定一个变量的类型是在您第一次给它赋值的时候。
  • 强类型语言

    一种总是强制类型定义的语言。Java 和 Python 是强制类型定义的。您有一个整数,如果不明确地进行转换 ,不能将把它当成一个字符串去应用。
  • 弱类型语言

    一种类型可以被忽略的语言,与强类型相反。JS 是弱类型的。在JS中,可以将字符串 '12' 和整数 3 进行连接得到字符串'123',然后可以把它看成整数 123 ,所有这些都不需要任何的显示转换。 所以说 Python 既是动态类型语言 (因为它不使用显示数据类型声明),又是强类型语言 (因为只要一个变量获得了一个数据类型,它实际上就一直是这个类型了)。
JavaScript和ECMAScript的关系

基础数据类型

一、数值:number

  • NaN:属于Number类型的一个特殊值,当遇到将字符串转成数字无效时,就会得到一个NaN数据
  • NaN参与的所有的运算都是false,除了!=
  • var b = 5/0; console.log(b); Infinity 无限大
  • ps:在JavaScript中,只要是数,就是数值型(number)的。无论整浮、浮点数(即小数)、无论大小、无论正负,都是number类型的。
  • var age = Number(18);
  • parseInt("1.2"); parseFloat("1.2");
<script>
var a = 100;            //定义了一个变量a,并且赋值100
console.log(typeof a);  //输出a变量的类型
console.log(typeof(a));  //输出a变量的类型

var num = 2.379;
var newNum = num.toFixed(2);   //小数的保留  四舍五入
console.log(newNum);  //2.38

var numStr = num.toString();
console.log(typeof numStr);  //string   //将number类型转换成字符串类型
</script>
View Code

二、字符串:string

  • var a = "abcde"; var b = '你好'; //不区分单双引号
  • + 如果加号两边都是数值,此时是数字的加号。否则就是连字符(用来连接字符串)。
  • var name = String("hello");
  • 常用方法:     obj.trim()     obj.charAt(index)     obj.substring(start,end)     obj.indexOf(char)     obj.length
方法 说明
.length   #不加括号的是属性 返回长度
.trim()    #得到一个新值 移除空白
.trimLeft() 移除左边的空白
.trimRight() 移除右边的空白
.concat(value, ...) #s1='hello';s.concat('xx');得到helloxx 拼接
.charAt(n) #n类似索引,从0开始,超过最大值返回''空字符串 返回第n个字符
[n] #n类似索引,可以使用方括号加数 字索引来访问字符串中的特定字符 返回第n个字符
.charCodeAt(n) #n类似索引,从0开始,超过最大值返回''空字符串 返回第n个字符的字符编码 类似python中的ord()
String.fromCharCode(n) #n代表数字,String.fromCharCode(101) 返回第101代表的字符 类似python中的chr()
.indexOf(substring, start) #这个start是从索引几开始找,没有返回-1 子序列位置
.substr(start, length) #第一个参数指定字符串的开始位置,第二个参数指定的则是返回的字符个数。如果没有给这些方法传递第二个参数,则将字符串的长度作为结束位置。 基于子字符串创建新字符串的方法
.substring(from, to) #不支持负数,所以一般都不用它,了解一下就行了 根据索引获取子序列
.slice(start, end) #var s1='helloworld';s1.slice(0,-5)看结果,就用它 切片
.toLowerCase() #全部变小写 小写
.toUpperCase()  #全部变大写 大写
.split(delimiter, limit)#分隔,s1.splite(' '),后面还可以加参数s1.split(' ',2),返回切割后的元素个数 分割

<script>
//创建方式
var str="hello world";
var str= new String("hello word");

//==================================================字符串位置方法
// chartAt(index) 返回指定索引的位置的字符
// charCodeAt()   String.fromCharCode
//[]

// indexOf  lastIndexOf  查询字符串位置  找不到则返回-1
//indexof()/search() 查找字符的下标,如果找到返回字符串的下标,找不到则返回-1 。

var str = 'hello world';

console.log(str.charAt(1));//e
console.log(str.charCodeAt(1)); //输出"101"
console.log(str.String.fromCharCode(101)); //输出"e"
console.log(str[1]);   //"e"


console.log(str.indexOf('m',0)); //-1
console.log(str.indexOf('o',0)); //4
console.log(str.indexOf('o',5)); //7

console.log(str.lastIndexOf('o')); //7

console.log(str.search('m')); //-1
console.log(str.search('o')); //4



//match(regexp)         match返回匹配字符串的数组,如果没有匹配则返回null
var a = 'hello,world';
var r = a.match("world");
console.log(r);   //["world", index: 6, input: "hello,world", groups: undefined]
var r = a.match("World");
console.log(r); //null

// 正则
var a = 'abcd,dcba';
var r = a.match(/\w+/);
console.log(r); //["abcd", index: 0, input: "abcd,dcba", groups: undefined]

// g表示匹配多次
var r = a.match(/\w+/g);
console.log(r); //(2) ["abcd", "dcba"]


//==================================================子字符串处理方法
//slice(start,end) 左闭右开 切片操作字符串
//substr(start, length)  start表示开始位置,length表示截取长度
//substring(indexStart,indexEnd) 切子字符串。顾头不顾尾  indexEnd是结束位置

var stringValue = "hello world";
alert(stringValue.slice(3));//"lo world"
alert(stringValue.substring(3));//"lo world"
alert(stringValue.substr(3));//"lo world"

alert(stringValue.slice(3, 7));//"lo w"
alert(stringValue.substring(3,7));//"lo w"
alert(stringValue.substr(3, 7));//"lo worl"   //第二个参数指定的是要返回的字符个数。


alert(stringValue.slice(-3));//"rld"         //相当于调用了  slice(11-3)   即slice(8)
alert(stringValue.substring(-3));//"hello world"   //相当于调用了substring(0)
alert(stringValue.substr(-3)); //"rld"       //相当于调用了  substr(11-3)   即substr(8)
alert(stringValue.slice(3, -4));//"lo w"     //相当于调用了  slice(3,11-4)   即slice(3,7)
alert(stringValue.substring(3, -4));//"hel"  //相当于调用了  substring(3, 0)
// 如果 indexStart 大于 indexEnd 则 substring 的执行效果就像两个参数调换了一样   即 substring(0, 3)
alert(stringValue.substr(3, -4)); //""(空字符串)  //substr()会将第二个参数转换为 0   即substr(3,0) 返回包含零个字符的字符串,也就是一个空字符串。

/*
参数:
    为正值时:slice(),slice(),substring()第一个参数指定字符串的开始位置
            slice()和 substring()的第二个参数指定的是字符串最后一个字符后面的位置。
            substr()的第二个参数指定的则是返回的字符个数。如果没有给这些方法传递第二个参数,则将字符串的长度作为结束位置。

    为负值时:slice()方法会将传入的负值与字符串的长度相加,
            substr()方法将负的第一个参数加上字符串的长度,而将负的第二个参数转换为 0
            substring()方法会把所有负值参数都转换为 0

            substring()两个参数相等,返回一个空字符串
                       没有参数时,返回整个字符串
                       如果第一个参数大于第二个参数,两个参数调换位置

*/

//==================================================字符串拼接方法
// concat 返回字符串值,表示两个或多个字符串的拼接一个新的字符串
var str1 = 'al';
var str2  = 'ex';
console.log(str1.concat(str2,str2));//alexex

//==================================================字符串替换方法
//replace(a,b) 将字符串a替换成字符串b
var a = '1234567755';
var newStr = a.replace("4567","****");
console.log(newStr);//123****755

//==================================================字符串分割方法
//split('a',1) 以字符串a分割字符串,并返回新的数组。如果第二个参数没写,表示返回整个数组,如果定义了个数,则返回数组的最大长度
var str1="一,二,三,四,五,六,日";
var strArray=str1.split(",");
console.log(strArray[1]);//结果为"二"

var str =  '我的天呢,a是嘛,你在说什么呢?a哈哈哈';
console.log(str.split('a'));//["我的天呢,", "是嘛,你在说什么呢?", "哈哈哈"]  如果第二个参数没写,表示返回整个数组
console.log(str.split('a',2));//["我的天呢,", "是嘛,你在说什么呢?"] 如果定义了个数,则返回数组的最大长度

//==================================================字符串其他方法
/*
toLocaleLowerCase()和 toLocaleUpperCase()方法则是针对特定地区的实现。
对有些地 区来说,针对地区的方法与其通用方法得到的结果相同,但少数语言(如土耳其语)
会为 Unicode 大小 写转换应用特殊的规则,这时候就必须使用针对地区的方法来保证实现正确的转换.
*/
var str="    hello world   ";
//length 获取字符串的长度
//trim() 去除字符串两边的空格
console.log(str.length,str.trim().length);  //18 11
//toLowerCase()转小写
var str = 'HELLO';
console.log(str.toLowerCase());//hello
//toUpperCase()转大写
var str = 'hello';
console.log(str.toUpperCase());//HELLO
</script>
View Code

三、布尔:boolean

  • var status = true;
  • var status = false;
  • var status = Boolen(1==1)

四、空对象:null

  • var c1 = null;//空对象 console.log(c1,typeof c1) // null "object"
  • 如果函数或方法要返回的是对象,那么找不到该对象时,返回的通常是 null。
  • 值undefined 实际上是从值null 派生来的,因此 ECMAScript 把它们定义为相等的。
  • undefined 是声明了变量但未对其初始化时赋予该变量的值,null 则用于表示尚未存在的对象

五、未定义:undefined

  • var d1; console.log(typeof d1) //undefined 表示变量未定义
  • 当声明的变量未初始化时,该变量的默认值是 undefined。
  • 当函数无明确返回值时,返回的也是值 "undefined"
  • undefined 是声明了变量但未对其初始化时赋予该变量的值,null 则用于表示尚未存在的对象

数据类型转换

  • 将string类型转换成number类型:
    1. parseInt() :字符串转数字
    2. parseFloat() : 字符串转小数
    3. Number()
  • 将number类型转换成string类型:
    1. 隐式转换: var n1 = 123;var n2 = '123';var n3 = n1+n2;console.log(typeof n3);
    2. 强制转换:String()和.toString:转字符串 var str1 = String(n1); 或 console.log(n1.toString())
  • Boolean():任何数据类型都可以转成布尔值
将number类型转换成string类型
隐式转换:
var n1 = 123;var n2 = '123';var n3 = n1+n2;console.log(typeof n3);
强制转换:String()或toString()
var str1 = String(n1); 或 console.log(n1.toString())

将string类型转换成number类型
Number:
var stringNum = '789.123wadjhkd'; console.log(Number(stringNum)); //NaN
parseInt()或parseFloat
console.log(parseInt(stringNum)); //789
console.log(parseFloat(stringNum)); //789.123

任何的数据类型都可以转换为boolean类型
console.log(Boolean(0));
console.log(Boolean(''));
console.log(Boolean(false));
console.log(Boolean(null));
console.log(Boolean(undefined));
console.log(Boolean(NaN));
console.log(Boolean(Infinity)); //true


<script>
//===================================parseInt() :字符串转数字
var a = '5';
var a = parseInt(a);
console.log(typeof(a));

//带有自动净化的功能;只保留字符串最开头的数字,后面的中文自动消失。
console.log(parseInt("2018你好!!")); //2018

//自动带有截断小数的功能:取整,不四舍五入。
var a = parseInt(5.8) + parseInt(4.7);
console.log(a);  //9
var a = parseInt(5.8 + 4.7);
console.log(a); //10

//===================================parseFloat() : 字符串转小数
var a = parseFloat('5.8')+ parseFloat('4.7');
console.log(a); //10.5
var a = parseFloat('5.8' + '4.7');
console.log(a); //5.84

//===================================Number
var stringNum = '789.123wadjhkd';
console.log(Number(stringNum)); //NaN

//===================================String()和.toString:转字符串 和 隐式转换
var n1 = 123;
var str1 = String(n1);
console.log(typeof str1); //string

var num = 234;
var str = num.toString();
console.log(num.toString(),typeof (num.toString()),str,typeof(str));

var n1 = 123;
var n2 = '123';
var n3 = n1+n2;
console.log(typeof n3, n3); //string 123123

//===================================Boolean():任何数据类型都可以转成布尔值
var b1 = '123';  // true
var b3 = -123;  // true
var b4 = Infinity; //表示正无穷大 true

var b2 = 0;       // false
var b5 = NaN;     //false
var b6;              //表示undefined //false
var b7 = null;    //false

console.log(Boolean(0));// false
console.log(Boolean(''));// false
console.log(Boolean(false));// false
console.log(Boolean(null));// false
console.log(Boolean(undefined));// false
console.log(Boolean(NaN));// false
console.log(Boolean(Infinity)); //true

</script>
View Code

内置对象类型

Javascript语言中的对象可以分为三种类型:

  • 用户定义对象(user-defined object):由程序员自行创建的对象。
  • 内建对象(native object):在JavaScript中除了null和undefined以外其他的数据类型都被定义成了对象,也可以用创建对象的方法定义变量,String、Math、Array、Date、RegExp都是JavaScript中重要的内置对象。
  • 宿主对象(host object):由浏览器提供的对象,比如window对象
类型内置对象介绍
数据对象 Number 数字对象
String 字符串对象
Boolean 布尔值对象
组合对象 Array 数组对象
Math 数学对象
Date 日期对象
组合对象 Object 自定义对象
Error 错误对象
Function 函数对象
RegExp 正则表达式对象
Global 全局对象
<script language="javascript">
var aa=Number.MAX_VALUE; 
//利用数字对象获取可表示最大数
var bb=new String("hello JavaScript"); 
//创建字符串对象
var cc=new Date();
//创建日期对象
var dd=new Array("星期一","星期二","星期三","星期四"); 
//数组对象
</script>
View Code

一、Array

方法 说明
.length 数组的大小
.push(ele) 尾部追加元素
.pop() 获取尾部的元素
.unshift(ele) 头部插入元素
.shift() 头部移除元素
.slice(start, end) 切片
.reverse() #在原数组上改的 反转
.join(seq)#a1.join('+'),seq是连接符 将数组元素连接成字符串
.concat(val, ...) #连个数组合并,得到一个新数组,原数组不变 连接数组
.sort()   排序
.forEach() #讲了函数再说 将数组的每个元素传递给回调函数
.splice() #参数:1.从哪删(索引), 2.删几个  3.删除位置替换的新元素(可多个元素) 删除元素,并向数组添加新元素。
.map()  #讲了函数再说 返回一个数组元素调用函数处理后的值的新数组

.filter()  #讲了函数再说 它利用指定的函数确定是否在返回的数组中包含某一项

<script type="text/javascript">
//==================================================创建方式
//方式1、字面量方式(推荐)
var colo = ['red','green','yellow'];
//方式2、使用构造函数(后面会讲)的方式创建 使用new关键词对构造函数进行创建对象,构造函数与后面的面向对象有关系
var colors = new Array();
//方式3、
var test=new Array(100,"a",true);

//创建二维数组:
var cnweek=new Array(7);
for (var i=0;i<=6;i++){
    cnweek[i]=new Array(2);
}
cnweek[0][0]="星期日";
cnweek[0][1]="Sunday";
cnweek[1][0]="星期一";
cnweek[1][1]="Monday";
//...
cnweek[6][0]="星期六";
cnweek[6][1]="Saturday";
console.log(cnweek);

//==================================================数组的赋值
//通过下标进行赋值
var arr=[];
arr[0] = 'red';
arr[1] = 'green';
arr[2] = 123;


//==================================================数组的合并 concat()
var north = ['北京','山东','天津'];
var south = ['东莞','深圳','上海'];
var newCity = north.concat(south);
console.log(newCity,typeof newCity);  //["北京", "山东", "天津", "东莞", "深圳", "上海"] "object"


var a = [1,2,3];
var b=a.concat(4,5) ;
console.log(a);  //返回结果为1,2,3
console.log(b,typeof b);  //返回结果为1,2,3,4,5  object

//==================================================join()
// 将数组中元素使用指定的字符串连接起来,它会形成一个新的字符串
var score = [98,78,76,100,0];
var str = score.join('|');
console.log(str,typeof str);//"98|78|76|100|0"  string
//==================================================数组的切片  slice(start, end)
// (左闭右开)  start表示开始位置索引  end是结束位置下一数组元素索引编号

/*
第一个数组元素索引为0
start、end可为负数,-1代表最后一个数组元素
end省略则相当于从start位置截取以后所有数组元素
*/
var arr1=['a','b','c','d','e','f','g','h'];
var arr2=arr1.slice(2,4);
var arr3=arr1.slice(4);
var arr4=arr1.slice(2,-1);

//==================================================toString方法:将数组转换成字符串
console.log(arr2.toString());//结果为"c,d"
console.log(arr3.toString());//结果为"e,f,g,h"
console.log(arr4.toString());//结果为"c,d,e,f,g"

var score = [98,78,76,100,0];
//toString() 直接转换为字符串  每个元素之间使用逗号隔开
var str = score.toString();
console.log(str);//98,78,76,100,0


//子数组操作  splice(start, deleteCount, value, ...)
/*
splice的主要用途是对数组指定位置进行删除和插入
start表示开始位置索引
deleteCount删除数组元素的个数
value表示在删除位置插入的数组元素
value参数可以省略
*/
var a = [1,2,3,4,5,6,7,8];
a.splice(1,2);
alert(a.toString());//a变为 [1,4,5,6,7,8]
a.splice(1,1);
alert(a.toString());//a变为[1,5,6,7,8]
a.splice(1,0,2,3);
alert(a.toString());//a变为[1,2,3,5,6,7,8]

//push pop这两个方法模拟的是一个栈操作,push压栈 pop弹栈
//==================================================pop 移除数组的最后一个元素
var arr = ['张三','李四','王文','赵六'];
var bb = arr.pop(); //返回值就是删除的元素
console.log(arr,bb);//["张三", "李四", "王文"] "赵六"

//==================================================push() 向数组最后添加一个元素,并返回新的长度
var arr = ['张三','李四','王文','赵六'];
var cc = arr.push('小马哥'); //可以添加多个,以逗号隔开  返回最终数组长度
console.log(arr,cc);//["张三", "李四","王文","赵六","小马哥"] 5

//==================================================shift和unshift:
//unshift():向数组的开头添加一个或更多元素,并返回新的长度
//shift():删除并返回数组的第一个元素
var arr1=[1,2,3];
var cc = arr1.unshift(4,5);
console.log(arr1,cc);  //结果为"4,5,1,2,3" 5
var dd=arr1.shift();
console.log(arr1,dd);  //结果为"5,1,2,3"  4

//==================================================reverse() 翻转数组
var names = ['alex','xiaoma','tanhuang','angle'];
names.reverse();
console.log(names); //["angle", "tanhuang", "xiaoma", "alex"]

//==================================================sort对数组排序
var names = ['alex','xiaoma','tanhuang','abngel'];
names.sort();
console.log(names); // ["alex", "angle", "tanhuang", "xiaoma"]
//数字的正确排序
/*
关于sort()需要注意:
      如果调用该方法时没有使用参数,将按字母顺序对数组中的元素进行排序,说得更精确点,是按照字符编码的顺序进行排序。要实现这一点,首先应把数组的元素都转换成字符串(如有必要),以便进行比较。
      如果想按照其他标准进行排序,就需要提供比较函数,也就是自己提供一个函数提供排序规则,该函数要比较两个值,然后返回一个用于说明这两个值的相对顺序的数字。比较函数应该具有两个参数 a 和 b,其返回值如下:
      若 a 小于 b,在排序后的数组中 a 应该出现在 b 之前,则返回一个小于 0 的值。
      若 a 等于 b,则返回 0。
      若 a 大于 b,则返回一个大于 0 的值。
*/
arr=[1,5,2,100];
arr.sort(intSort);
function intSort(a,b){
    return a-b;
}
console.log(arr); //1,2,5,100

//==================================================清空数组的几种方式
var array = [1,2,3,4,5,6];
array.splice(0);      //方式1:删除数组中所有项目
array.length = 0;     //方式1:length属性可以赋值,在其它语言中length是只读
array = [];           //方式3:推荐


//判断是否为数组:isArray()  布尔类型值 = Array.isArray(被检测的值)
var test = [1,'a',3];
console.log(Array.isArray(test));  //true


//js中数组的特性
//特性1: js中的数组可以装任意类型,没有任何限制.
//特性2: js中的数组,长度是随着下标变化的.用到多长就有多长.
var arr5 = ['abc',123,1.14,true,null,undefined,new String('1213'),new Function('a','b','alert(a+b)')];
alert(arr5.length);//8
arr5[10] = "hahaha";
alert(arr5.length); //11
alert(arr5[9]);// undefined

//==================================================forEach方法,设定每一个元素执行某函数
var arr = ['alex','wusir'];
    arr.forEach(fn);
    function fn(item,index){
        console.log(index,item)
    }
/*
0 "alex"
1 "wusir"
*/

var numbers = [1,2,3,4,5,4,3,2,1];
numbers.forEach(function(item, index, array){
    console.log(index,item,array)
});

//==================================================map方法,循环每一个元素完成某操作返回新的值组成新数组
var arr = ['alex','wusir'];
    var ret = arr.map(fn);
    function fn(item,index) {
        console.log(index,item);
        return item+'sb'
    }
    console.log(ret);
/*
0 "alex"
1 "wusir"
["alexsb", "wusirsb"]
*/

var numbers = [1,2,3,4,5,4,3,2,1];
var filterResult = numbers.map(function(item, index, array){
    return item * 2;
});
alert(filterResult); //[2,4,6,8,10,8,6,4,2]

//==================================================filter 它利用指定的函数确定是否在返回的数组中包含某一项。
// 例如要返回一个所有数值都大于2的数组,可以使用如下代码。
var numbers = [1,2,3,4,5,4,3,2,1];
var filterResult = numbers.filter(function(item, index, array){
    return (item > 2);
});
alert(filterResult); //[3,4,5,4,3]


//==================================================toString()方法会返回由数组中每个值的字符串形式拼接而成的一个以逗号分隔的字符串。

var colors = ['red','green','blue'];
alert(colors.toString());//red,green,blue


var person1 = {
    toLocaleString : function () {
        return '马大帅';
    },
    toString : function () {
        return '马小帅';
    }
};
var person2 = {
    toLocaleString : function () {
        return '张大帅';
    },
    toString : function () {
        return '张小帅';
    }
};
var people = [person1,person2];
alert(people);//马小帅,张小帅
alert(people.toString());//马小帅,张小帅
alert(people.toLocaleString()); //马大帅,张大帅
</script>
View Code

二、Math

方法 说明
Math.floor() 向下取整,称为“地板函数”
Math.ceil() 向上取整,称为“天花板函数”
Math.max(a,b) 求a和b中的最大值
Math.min(a,b) 求a和b中的最小值
Math.random() 随机数,默认0-1之间的随机数,公式min+Math.random()*(max-min),求min-max之间的数
Math.E 自然对数的底数,即常量e的值
Math.LN10 10的自然对数 ln(10)
Math.LN2 2的自然对数
Math.LOG2E 以2为底e的对数
Math.LOG10E 以10为底e的对数
Math.PI π的值
Math.SQRT1_2 1/2的平方根(即2的平方根的倒数)
Math.SQRT2 2的平方根
<script type="text/javascript">

//该对象中的属性方法 和数学有关.
/*
abs(x)    返回数的绝对值。
exp(x)    返回 e 的指数。
floor(x)对数进行下舍入。
log(x)    返回数的自然对数(底为e)。
max(x,y)    返回 x 和 y 中的最高值。
min(x,y)    返回 x 和 y 中的最低值。
pow(x,y)    返回 x 的 y 次幂。
random()    返回 0 ~ 1 之间的随机数。
round(x)    把数四舍五入为最接近的整数。
sin(x)    返回数的正弦。
sqrt(x)    返回数的平方根。
tan(x)    返回角的正切。
*/

console.log(Math.pow(2,4));// 16  pow 计算参数1 的参数2 次方.
console.log(Math.round(1.5)); // 2  四舍五入

// Math.ceil() 向上取整,'天花板函数'
var x = 1.234;
//天花板函数  表示大于等于 x,并且与它最接近的整数是2
var a = Math.ceil(x);
console.log(a,x);//2  1.234

// Math.floor 向下取整,'地板函数'
var x = 1.234;
// 小于等于 x,并且与它最接近的整数 1
var b = Math.floor(x);
console.log(b,x);//1  1.234

//求两个数的最大值和最小值
console.log(Math.max(2,5));//5
console.log(Math.min(2,5));//2

//随机数 Math.random() 获得随机数 0~1不包括1.
var ran = Math.random();
console.log(ran);  //  min - max之间的随机数: min+Math.random()*(max-min);

//获取随机颜色
/**
 * 产生一个随机的rgb颜色
 * @return {String}  返回颜色rgb值字符串内容,如:rgb(201, 57, 96)
 */
function randomColor() {
    // 随机生成 rgb 值,每个颜色值在 0 - 255 之间
    var r = Math.floor(Math.random()*255),
        g = Math.floor(Math.random()*255),
        b = Math.floor(Math.random()*255);
    // 连接字符串的结果
    var result = "rgb("+ r +","+ g +","+ b +")";
    // 返回结果
    return result;
}
console.log(randomColor())


//获取随机验证码
function createCode(){
    //首先默认code为空字符串
    var code = '';
    //设置长度,这里看需求,我这里设置了4
    var codeLength = 4;
    //设置随机字符
    var random = new Array(0,1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R', 'S','T','U','V','W','X','Y','Z');
    //循环codeLength 我设置的4就是循环4次
    for(var i = 0; i < codeLength; i++){
        //设置随机数范围,这设置为0 ~ 36
        var index = Math.floor(Math.random()*36);
        //字符串拼接 将每次随机的字符 进行拼接
        code += random[index];
    }
    //将拼接好的字符串赋值给展示的Value
    return code
}
console.log(createCode())




//要找到数组中最大或最小值,可以像下面这样用apply()方法
var arr = [1,2,36,23,43,3,41];
var max_item = Math.max.apply(null, arr);
//这个技巧的关键是把 Math 对象作为 apply()的第一个参数,从而正确地设置 this 值。然后,可以将任何数组作为第二个参数。
console.log(max_item); //43

</script>
View Code

三、Date

方法 说明
getDate() 根据本地时间返回指定日期对象的月份中的第几天(1-31)
Date() 根据本地时间返回当天的日期和时间
getMonth() 根据本地时间返回指定日期对象的月份(0-11)
getFullYear() 根据本地时间返回指定日期对象的年份(四位数年份时返回四位数字)
getDay() 根据本地时间返回指定日期对象的星期中的第几天(0-6)
getHours() 根据本地时间返回指定日期对象的小时(0-23)
getMinutes() 根据本地时间返回指定日期对象的分钟(0-59)
getSeconds() 根据本地时间返回指定日期对象的秒数(0-59)
<script type="text/javascript">
//方法1:不指定参数
var nowd1=new Date();
console.log(nowd1.toLocaleString( ));
//方法2:参数为日期字符串
var nowd2=new Date("2004/3/20 11:12"); //2004/3/20 上午11:12:00
console.log(nowd2.toLocaleString( ));
var nowd3=new Date("04/03/20 11:12");
console.log(nowd3.toLocaleString( )); //2020/4/3 上午11:12:00
//方法3:参数为毫秒数
var nowd3=new Date(5000);
console.log(nowd3.toLocaleString( )); //1970/1/1 上午8:00:05
console.log(nowd3.toUTCString()); //Thu, 01 Jan 1970 00:00:05 GMT
//方法4:参数为年月日小时分钟秒毫秒
var nowd4=new Date(2004,3,20,11,12,0,300);
console.log(nowd4.toLocaleString( ));//毫秒并不直接显示 2004/4/20 上午11:12:00
 
//Date对象的方法—获取日期和时间
/*
getDate()                 获取日
getDay ()                 获取星期
getMonth ()               获取月(0-11)
getFullYear ()            获取完整年份
getYear ()                获取年
getHours ()               获取小时
getMinutes ()             获取分钟
getSeconds ()             获取秒
getMilliseconds ()        获取毫秒
getTime ()                返回累计毫秒数(从1970/1/1午夜)
*/
 
function getCurrentDate(){
        //1. 创建Date对象
        var date = new Date(); //没有填入任何参数那么就是当前时间
        //2. 获得当前年份
        var year = date.getFullYear();
        //3. 获得当前月份 js中月份是从0到11.
        var month = date.getMonth()+1;
        //4. 获得当前日
        var day = date.getDate();
        //5. 获得当前小时
        var hour = date.getHours();
        //6. 获得当前分钟
        var min = date.getMinutes();
        //7. 获得当前秒
        var sec = date.getSeconds();
        //8. 获得当前星期
        var week = date.getDay(); //没有getWeek
        // 2014年06月18日 15:40:30 星期三
        return year+"年"+changeNum(month)+"月"+day+"日 "+hour+":"+min+":"+sec+" "+parseWeek(week);
    }
 
alert(getCurrentDate());
 
//解决 自动补齐成两位数字的方法
    function changeNum(num){
    if(num < 10){
        return "0"+num;
    }else{
        return num;
    }
}
//将数字 0~6 转换成 星期日到星期六
    function parseWeek(week){
    var arr = ["星期日","星期一","星期二","星期三","星期四","星期五","星期六"];
    return arr[week];
}
//Date对象的方法—设置日期和时间
/*
setDate(day_of_month)       设置日
setMonth (month)            设置月
setFullYear (year)          设置年
setHours (hour)         设置小时
setMinutes (minute)     设置分钟
setSeconds (second)     设置秒
setMillliseconds (ms)       设置毫秒(0-999)
setTime (allms)     设置累计毫秒(从1970/1/1午夜)
*/
var x=new Date();
x.setFullYear (1997);    //设置年1997

x.setMonth(7);        //设置月7
x.setDate(1);        //设置日1
x.setHours(5);        //设置小时5
x.setMinutes(12);    //设置分钟12
x.setSeconds(54);    //设置秒54
x.setMilliseconds(230);        //设置毫秒230
document.write(x.toLocaleString( )+"<br>");
//返回1997年8月1日5点12分54秒
 
x.setTime(870409430000); //设置累计毫秒数
document.write(x.toLocaleString( )+"<br>");
//返回1997年8月1日12点23分50秒
 
//Date对象的方法—日期和时间的转换
getTimezoneOffset();//8个时区×15度×4分/度=480; 返回本地时间与GMT的时间差以分钟为单位
toUTCString(); //返回国际标准时间字符串
toLocalString(); //返回本地格式时间字符串
Date.parse(x);//返回累计毫秒数(从1970/1/1午夜到本地时间)
Date.UTC(x);//返回累计毫秒数(从1970/1/1午夜到国际时间)
</script>
View Code

四、RegExp

  • 在线测试工具https://regexr.com/
  • unicode转中文站点:http://www.bejson.com/convert/unicode_chinese/
<script type="text/javascript">

/*

正则表达式(Regular Expression)是一门简单语言的语法规范,是强大、便捷、高效的文本处理工具,
它应用在一些方法中,对字符串中的信息实现查找、替换和提取操作

javascript中的正则表达式用RegExp对象表示,有两种写法:一种是字面量写法;另一种是构造函数写法

字面量写法

正则表达式字面量定义为包含在一对斜杠(/)之间的字符,并且可以设置3个标志

var expression = /pattern/flags;

正则表达式的匹配模式支持下列3个标志:
g:表示全局(global)模式,即模式将被应用于所有字符串,而并非在发现第一个匹配项时立即停止
i:表示不区分大小写(case-insensitive)模式,即在确定匹配项时忽略模式与字符串的大小写
m:表示多行(multiline)模式,即在到达一行文本末尾时还会继续查找下一行中是否存在与模式匹配的项
//匹配字符串所有'at'的实例
var p = /at/g;
//test()方法返回一个布尔值表示是否可以找到匹配项
console.log(p.test('ata'));//true
console.log(p.test('aba'));//false


RegExp构造函数
和普通的内置对象一样,RegExp正则表达式对象也支持new RegExp()构造函数的形式
RegExp构造函数接收两个参数:要匹配的字符串模式(pattern)和可选的标志字符串(flags),标志字符串和字面量的三个标志含义相同:’g’、’i’、’m’。
RegExp构造函数的两个参数都是字符串。且使用字面量形式定义的任何表达式都可使用构造函数

//匹配字符串所有'at'的实例
var p1 = /at/g;
//同上
var p2 = new RegExp('at','g');



匹配规则

1.元字符

匹配任意一个字符(.)

点字符(.)匹配除回车(\r)、(\n)、行分隔符(\u2028)和段分隔符(\u2029)以外的所有字符


位置字符(^和$)

位置字符用来提示字符所处的位置,主要有两个字符

^表示字符串的开始位置
$表示字符串的结束位置
//hello必须出现在开始的位置
/^hello/g.test('hello world');//true
//hello必须出现在结束位置
/world$/g.test('hello world');//true
//从开始位置到结束位置只有hello
/^hello$/g.test('hello');//true
/^hello$/g.test('hello world');//false

选择符(|)

竖线符号(|)在正则表达式中表示“或关系”(OR)。即you|me表示匹配you或me

//正则表达式指定必须匹配11或22
/ll|yy/g.test('hello world');//true
多个选择符可以联合使用

//匹配cat、dog、pig之中的一个
/cat|dog|pig/g
匹配数字和字母以及非字母(\w和\W)

\w匹配数字和字母
注意:下划线\w也匹配到

\W匹配除数字和字母以及下划线以外的任意字符



匹配数字和非数字(\d和\D)

\d只匹配数字,0~9之间的数字

\D匹配除数字以外的任意字符

匹配空白字符\s和\S

\d匹配空白字符

\D匹配非空白字符

字符集合[a-zA-ZO-9]

[a-zA-Z0-9]匹配[]里面的任意字符


如果想匹配任意一个中文怎么办?

中文的范围区间为[\u4e00-\u9fa5] ,其实就是[一-龥(yu)]

unicode转中文站点:http://www.bejson.com/convert/unicode_chinese/



[^]匹配所有不在这范围内的字符

匹配了所有不在[一-龥]的之间的字符

重复一次或多次(+)

匹配重复0个或多个字符(*)

匹配重复0个或一个(?)

指定重复范围({min,max})

分组匹配()

console.log(/w{3}\.(baidu|goole|mi|apeland)\.(com|cn)?/.test('www.baidu.com'));
console.log(RegExp.$1);//baidu
console.log(RegExp.$2);//com

分组匹配但不捕获(?:)

console.log(/w{3}\.(baidu|goole|mi|apeland)\.(?:com|cn)?/.test('www.baidu.com'));
console.log(RegExp.$1);//baidu
console.log(RegExp.$2);


*/


// 正则表达式常用案例

// 1.检查用户账号
// 验证规则: 由字母,数字,下划线 组成,以字母开头 4-16位
function checkUser(str) {
    // 模式
    var re = /^[a-zA-Z]\w{3,15}$/g;
    if (re.test(str)) {
        return '正确';
    } else {
        return '错误';
    }
}
console.log(checkUser('h111'));
// 2.匹配手机号
// 验证规则:
// 11位,以1开头 第二位为3或5或7或8  
function checkMobild(str){
    var re = /^1[3|5|7|8]\d{9}/g;
    if(re.test(str)){
        alert('正确');
    }else{
        alert('错误');
    }
}
checkMobild('1467654321213'); 
// 3.匹配电话号码
// 验证规则: 区号 + 号码
//- 可有可无 区号可以为3位也可以为4位 号码可以为7位也可以为8位
// 01088888888 010-8888888 0536-7777777
function checkPhone(str) {
    var re = /^0\d{2,3}-?\d{7,8}$/g;
    if (re.test(str)) {
        console.log('匹配正确');
    } else {
        console.log('匹配错误');
    }
}
checkPhone('0536-7777777');
// 4.匹配身份证号
// 18位或者15位,15位全是数字 18位 前17都是数字,最后一位可能是数字或者字符x或X
function checkCard(card) {
    var re = /^(\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/g;
    if (re.test(card)) {
        console.log('身份证输入合法');
    } else {
        console.log('身份证输入不合法');
    }
}
checkCard('76499819909876543');
// 5.匹配邮箱
// 第一部分@第二部分.com|cn|net
function checkEmail(str){
    var re = /^[a-zA-Z0-9-._]+[@][a-zA-Z0-9-._]+\.(com|cn|net)$/g;
    if(re.test(str)){
        console.log('输入邮箱格式正确');
    }else{
        console.log('输入邮箱格式错误');
    }
}
checkEmail('1ehjdjh@qq.com');

</script>
RegExp
//RegExp对象

//创建正则对象方式1
// 参数1 正则表达式(不能有空格)
// 参数2 匹配模式:常用g(全局匹配;找到所有匹配,而不是在第一个匹配后停止)和i(忽略大小写)

// 用户名只能是英文字母、数字和_,并且首字母必须是英文字母。长度最短不能少于6位 最长不能超过12位。

// 创建RegExp对象方式(逗号后面不要加空格),假如匹配用户名是只能字母开头后面是字母加数字加下划线的5到11位的
var reg1 = new RegExp("^[a-zA-Z][a-zA-Z0-9_]{5,11}$"); //注意,写规则的时候,里面千万不能有空格,不然匹配不出来你想要的内容,除非你想要的内容本身就想要空格,比如最后这个{5,11},里面不能有空格

// 匹配响应的字符串
var s1 = "bc123";

//RegExp对象的test方法,测试一个字符串是否符合对应的正则规则,返回值是true或false。
reg1.test(s1);  // true

// 创建方式2,简写的方式
// /填写正则表达式/匹配模式(逗号后面不要加空格)
var reg2 = /^[a-zA-Z][a-zA-Z0-9_]{5,11}$/; 

reg2.test(s1);  // true

注意,此处有坑:如果你直接写一个reg2.test(),test里面啥也不传,直接执行,会返回一个true,用其他的正则规则,可能会返回false,是因为,test里面什么也不传,默认传的是一个undefined,并且给你变成字符串undefined,所以能够匹配undefined的规则,就能返回true,不然返回false


// String对象与正则结合的4个方法
var s2 = "hello world";

s2.match(/o/g);         // ["o", "o"]             查找字符串中 符合正则 的内容 ,/o/g后面这个g的意思是匹配所有的o,
s2.search(/h/g);        // 0                      查找字符串中符合正则表达式的内容位置,返回第一个配到的元素的索引位置,加不加g效果相同
s2.split(/o/g);         // ["hell", " w", "rld"]  按照正则表达式对字符串进行切割,得到一个新值,原数据不变
s2.replace(/o/g, "s");  // "hells wsrld"          对字符串按照正则进行替换

// 关于匹配模式:g和i的简单示例
var s1 = "name:Alex age:18";

s1.replace(/a/, "哈哈哈");      // "n哈哈哈me:Alex age:18"
s1.replace(/a/g, "哈哈哈");     // "n哈哈哈me:Alex 哈哈哈ge:18"      全局匹配
s1.replace(/a/gi, "哈哈哈");    // "n哈哈哈me:哈哈哈lex 哈哈哈ge:18"  不区分大小写


// 注意事项1:
// 如果regExpObject带有全局标志g,test()函数不是从字符串的开头开始查找,而是从属性regExpObject.lastIndex所指定的索引处开始查找。
// 该属性值默认为0,所以第一次仍然是从字符串的开头查找。
// 当找到一个匹配时,test()函数会将regExpObject.lastIndex的值改为字符串中本次匹配内容的最后一个字符的下一个索引位置。
// 当再次执行test()函数时,将会从该索引位置处开始查找,从而找到下一个匹配。
// 因此,当我们使用test()函数执行了一次匹配之后,如果想要重新使用test()函数从头开始查找,则需要手动将regExpObject.lastIndex的值重置为 0。
// 如果test()函数再也找不到可以匹配的文本时,该函数会自动把regExpObject.lastIndex属性重置为 0。

var reg3 = /foo/g;
// 此时 regex.lastIndex=0
reg3.test('foo'); // 返回true
// 此时 regex.lastIndex=3
reg3.test('xxxfoo'); // 还是返回true
// 所以我们在使用test()方法校验一个字符串是否完全匹配时,一定要加上^和$符号,把匹配规则写的确定一些,尽量不用上面这种的写法/xxx/。

// 注意事项2(说出来你可能不信系列):
// 当我们不加参数调用RegExpObj.test()方法时, 相当于执行RegExpObj.test(undefined),然后将这个undefined又转为字符串"undefined",去进行匹配了, 并且/undefined/.test()默认返回true。
var reg4 = /^undefined$/;
reg4.test(); // 返回true
reg4.test(undefined); // 返回true
reg4.test("undefined"); // 返回true
View Code

五、Function

  • 实际参数和形式参数的个数,要相同。
  • 伪数组:arguments代表的是实参。有个讲究的地方是:arguments只在函数中使用。返回函数实参的个数:arguments.length
  • ​函数中的return语句用来返回函数调用后的返回值,如果函数没有返回值默认返回值就是undefined,python中是None
  • ​如果函数调用时在前面加上了new前缀,且返回值不是一个对象或者没有返回值,则返回this(该新对象)
  • 函数只能返回一个值,如果要返回多个值,只能将其放在数组或对象中返回。
  • 可以使用变量、常量或表达式作为函数调用的参数
  • 函数名的定义规则与标识符一致,大小写是敏感的
  • 注意:js的函数加载执行与python不同,它是整体加载完才会执行,所以执行函数放在函数声明上面或下面都可以,python 中必须先声明后使用
  • 闭包:https://www.jianshu.com/p/26c81fde22fb
<script>
//注意:js的函数加载执行与python不同,它是整体加载完才会执行,所以执行函数放在函数声明上面或下面都可以:
//声明方式1: 函数的声明语句Function Declaration
function add(x,y){
    return x+y;
}
//js中调用函数
console.log(add(1,2));

//声明方式2:函数表达式(变量赋值) 这个匿名函数又称函数表达式(Function Expression)因为赋值语句的等号右侧只能放表达式。
var hello = function(a){
    console.log(a)
};

//注意:采用函数表达式声明函数时,function命令后面不带有函数名。如果加上函数名,该函数名只在函数体内部有效,在函数体外部无效。
var hi = function x(a){
    console.log(typeof x);
};
console.log(x);// ReferenceError: x is not defined
hi(); //function



//声明方式3:Function构造函数 (几乎无人使用)
var func2=new Function("name","alert(\"hello\"+name);");
func2("tom");

//只要函数名写对即可,参数怎么填都不报错.
function func1(a,b){
    console.log(a+b);
}
func1(1,2);  //3
func1(1,2,3);//3
func1(1);    //NaN
func1();     //NaN



// 返回值:
var test = function fn(){};
console.log(test());//undefined

var test2 = function fn(){
    return;
};
console.log(test2()); //undefined;   python中也是一样作业写的话就返回None


//​ 如果函数调用时在前面加上了new前缀,且返回值不是一个对象或者没有返回值,则返回this(该新对象)
function fn(){
    this.a = 2;
    return 1;
}
var test = new fn();

console.log(test);//fn{a:2}
console.log(test.constructor);//fn(){this.a = 2;return 1;}


function fn2(){
    this.a = 2;
    return {a:1};
}
var test = new fn2();
console.log(test);//{a:1}
console.log(test.constructor);//Object() { [native code] }



function testFinally(){
    try{
        return 2;
    }catch(error){
        return 1;
    }finally{
        return 0;
    }
}
//注意:并不是函数中return语句后的所有语句都不执行,finally语句例外,return语句不会阻止finally子句的执行
console.log(testFinally())  // 0

/*
try{
    //通常来讲,这里的代码会从头到尾而不会产生任何问题
    //但有时会抛出一个异常,要么是由throw语句直接抛出,要么通过调用一个方法间接抛出
}catch(e){
    //当且仅当try语句块抛出了异常,才会执行这里的代码
    //这里可以通过局部变量e来获得对Error对象或者抛出的其他值的引用
    //这里的代码块可以基于某种原因处理这个异常,也可以忽略这个异常,还可以通过throw语句重新抛出异常
}finally{
    //不管try语句是否抛出了异常,finally里的逻辑总是会执行,终止try语句块的方式有:
    //1、正常终止,执行完语句块的最后一条语句
    //2、通过break、continue或return语句终止
    //3、抛出一个异常,异常被catch从句捕获
    //4、抛出一个异常,异常未被捕获,继续向上传播
}
*/

</script>
函数声明和返回值
<script>
/*
javascript函数的参数与大多数其他语言的函数的参数有所不同。函数不介意传递进来多少个参数,也不在乎传进来的参数是什么数据类型,甚至可以不传参数
javascript中的函数定义并未指定函数形参的类型,函数调用也未对传入的实参值做任何类型检查。实际上,javascript函数调用甚至不检查传入形参的个数。
*/
function add(x){
    return x+1;
};
console.log(add(1));//2
console.log(add('1'));//'11'
console.log(add());//NaN
console.log(add(1,2));//2

//同名形参: 在非严格模式下,函数中可以出现同名形参,且只能访问最后出现的该名称的形参
function add(x,x,x){
    return x;
}
console.log(add(1,2,3));//3

//同名形参: 在严格模式下,出现同名形参会抛出语法错误
function add(x,x,x){
    'use strict';
    return x;
}
console.log(add(1,2,3));//SyntaxError: Duplicate parameter name not allowed in this context

//参数个数:当实参比函数声明指定的形参个数要少,剩下的形参都将设置为undefined值
function add(x,y){
    console.log(x,y);//1 undefined
}
add(1);

//参数个数:当实参比形参个数要多时,剩下的实参没有办法直接获得,需要使用arguments对象来获取

/*
javascript中的参数在内部用一个数组表示。函数接收到的始终都是这个数组,而不关心数组中包含哪些参数。
在函数体内可以通过arguments对象来访问这个参数数组,从而获取传递给函数的每一个参数。arguments对象并不是Array的实例,
它是一个类数组对象,可以使用方括号语法访问它的每一个元素。
*/
function add(x){
    console.log(arguments[0],arguments[1],arguments[2])//1 2 3
    return x+1;
}
add(1,2,3);

//形参只是提供便利,但不是必需的
function add(){
    return arguments[0] + arguments[1];
}
console.log(add(1,2));//3



// arguments对象的length属性显示实参的个数,函数的length属性显示形参的个数
// 1、伪数组 arguments arguments代表的是实参。arguments只在函数中使用。
fn(2,4);
fn(2,4,6);
fn(2,4,6,8);
function fn(a,b,c) {
    console.log(arguments);
    console.log(fn.length);         //获取形参的个数  一直是3
    console.log(arguments.length);  //获取实参的个数
    console.log("----------------");
}
// 2、arguments是伪数组,是因为:arguments可以修改元素,但不能改变数组的长短。
function fn2(a,b) {
    arguments[0] = 99;  //将实参的第一个数改为99
    //arguments.push(8);  //报错,因为无法增加元素  TypeError: arguments.push is not a function
}

//arguments的用处1:
function nxAdd(){
    var result=0;
    for (var num in arguments){
        result+=arguments[num]
    }
    alert(result);
}
nxAdd(1,2,3,4,5);

//arguments的用处2:
function f(a,b,c){
    if(arguments.length!=3){
        throw new Error("function f called with "+arguments.length+" arguments,but it just need 3 arguments")
    }
    else {
        alert("success!")
    }
}
f(1,2,3,4,5);


//通过检查传入函数中参数的类型和数量并作出不同的反应,来模仿方法的重载
function doAdd(){
    if(arguments.length == 1){
        alert(arguments[0] + 10);
    }else if(arguments.length == 2){
        alert(arguments[0] + arguments[1]);
    }
}
doAdd(10);//20
doAdd(30,20);//50



//参数传递
/*
javascript中所有函数的参数都是按值传递的。也就是说,把函数外部的值复制到函数内部的参数,就和把值从一个变量复制到另一个变量一样
在向参数传递基本类型的值时,被传递的值会被复制到一个局部变量(命名参数或arguments对象的一个元素)
1.基本类型值
*/

function addTen(num){
    num += 10;
    return num;
}
var count = 20;
var result = addTen(count);
console.log(count);//20,没有变化
console.log(result);//30



/*
2.引用类型值
在向参数传递引用类型的值时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化会反映在函数的外部
*/

function setName(obj){
    //obj 在函数内部是一个局部变量
    obj.name = 'test';
}
var person = new Object();
setName(person);
console.log(person.name);//'test'


//当在函数内部重写引用类型的形参时,这个变量引用的就是一个局部对象了。而这个局部对象会在函数执行完毕后立即被销毁
function setName(obj){
    obj.name = 'test';
    console.log(person.name);//'test'
    //函数内部重写引用类型的形参,这个obj变量是一个局部对象。函数执行完毕,立马销毁。所以person.name还是全局对象person的属性值
    obj = new Object();
    obj.name = 'white';
    console.log(person.name);//'test'
}
var person = new Object();
setName(person);


</script>
函数参数
<script type="text/javascript">
//javascript一共有4种调用模式:函数调用模式、方法调用模式、构造器调用模式和间接调用模式
/*
1.函数调用模式:
当一个函数并非一个对象的属性时,那么它就是被当做一个函数来调用。对于普通的函数调用来说,函数的返回值就是调用表达式的值
使用函数调用模式调用函数时,非严格模式下,this被绑定到全局对象;在严格模式下,this是undefined

*/
function add(x,y){
    'use strict';
    console.log(this);//undefined
}
add();

function add(x,y){
    console.log(this);//window
}
add();


//因为函数调用模式中的this绑定到全局对象,所以会发生全局属性被重写的现象
var a = 0;
function fn(){
    this.a = 1;
}
fn();
console.log(this,this.a,a);//window 1 1


/*
2.方法调用模式
当一个函数被保存在对象的一个属性时,我们称它为一个方法。当一个方法被调用时,this被绑定到该对象。
如果调用表达式包含一个提取属性的动作,那么它就是被当做一个方法来调用

方法可以使用this访问自己所属的对象,所以它能在对象中取值或对对象进行修改。this到对象的绑定发生在调用时候。
通过this取得他们所属对象的上下文的方法称之为公共方法。
*/

var o = {
    m:function(){
        console.log(111);
    }
};
o.m(); //111


var p = {
    a:1,
    m:function(){
        console.log(111);
    },
    n:function(){
        this.a = 2;
        return this;
    }
};

console.log(p.a); //1
console.log(p.n().a);//2
console.log(p.a); //2

/*
3.构造函数调用模式
如果函数或者方法调用之前带有关键字new,它就构成构造函数调用
*/
function fn(){
    this.a = 1;
};
var obj = new fn();
console.log(obj.a);//1


//如果构造函数调用在圆括号内包含一组实参列表,先计算这些实参表达式,然后传入函数内
function fn(x){
    this.a = x;
};
var obj = new fn(2);
console.log(obj.a);//2


//如果构造函数没有形参,javascript构造函数调用的语法是允许省略实参列表和圆括号的。凡是没有形参的构造函数调用都可以省略圆括号
var o = new Object();
//等价于
var o = new Object;


//构造函数通常不适用return关键字,他们通常初始化新对象,当构造函数的函数体执行完毕时,它会显式返回。在这种情况下,构造函数调用表达式的计算结果就是这个新对象的值。
function fn(){
    this.a = 2;
}
var test = new fn();
console.log(test);//fn {a: 2}

//如果构造函数使用return语句但没有指定返回值,或者返回一个原始值,那么这时将忽略返回值,同时使用这个新对象作为调用结果
function fn(){
    this.a = 2;
    return;
}
var test = new fn();
console.log(test);//fn {a: 2}

//如果构造函数显式地使用return语句返回一个对象,那么调用表达式的值就是这个对象
var obj = {a:1};
function fn(){
    this.a = 2;
    return obj;
}
var test = new fn();
console.log(test);//{a:1}


/*
4.间接调用模式
javascript中函数也是对象,函数对象也可以包含方法。call()和apply()方法可以用来间接地调用函数
这两个方法都允许显式指定调用所需的this值,也就是说,任何函数可以作为任何对象的方法来调用,哪怕这个函数不是那个对象的方法。
两个方法都可以指定调用的实参。call()方法使用它自有的实参列表作为函数的实参,apply()方法则要求以数组的形式传入参数.
*/
var obj = {};
function sum(x,y){
    return x+y;
}
console.log(sum.call(obj,1,2));//3
console.log(sum.apply(obj,[1,2]));//3

        
</script>
函数的调用方式
<script>
/*
函数属性和方法
函数是javascript中特殊的对象,可以拥有属性和方法,就像普通的对象拥有属性和方法一样。甚至可以用Function()构造函数来创建新的函数对象。
*/

/*length属性:
arguments对象的length属性表示实参个数,而参数的length属性则表示形参个数
*/
function add(x,y){
    console.log(arguments.length);//3
    console.log(add.length);//2
}
add(1,2,3);

/*
name属性:
函数定义了一个非标准的name属性,通过这个属性可以访问到给定函数指定的名字,这个属性的值永远等于跟在function关键字后面的标识符,匿名函数的name属性为空
*/
function fn(){};
console.log(fn.name);//'fn'
var fn = function(){};
console.log(fn.name);//'fn'
var fn = function abc(){};
console.log(fn.name);//'abc'

/*
prototype属性:
每一个函数都有一个prototype属性,这个属性指向了一个对象的引用,这个对象叫做原型对象(prototype object)。
每一个函数都包含不同的原型对象。将函数用作构造函数时,新创建的对象会从原型对象上继承属性。
*/
function fn(){};
var obj = new fn;
fn.prototype.a = 1;
console.log(obj.a);//1


/*
方法:
apply()和call()
每一个函数都包含两个非继承而来的方法:apply()和call()方法。这两个方法的用途都是在特定的作用域中调用函数,实际上等于函数体内this对象的值。
要想以对象o的方法来调用函数f(),可以使用call()和apply()
f.call(o);  f.apply(o);


假设o中不存在m方法,则等价于:
o.m = f;//将f存储为o的临时方法
o.m();//调用它,不传入参数
delete o.m; //将临时方法删除
*/

window.color = "red";
var o = {color: "blue"};
function sayColor(){
    console.log(this.color);
}
sayColor();            //red
sayColor.call(this);   //red
sayColor.call(window); //red
sayColor.call(o);      //blue

//sayColor.call(o)等价于:
o.sayColor = sayColor;
o.sayColor();   //blue
delete o.sayColor;


/*
apply()方法接收两个参数:一个是在其中运行函数的作用域(或者可以说成是要调用函数的母对象,它是调用上下文,
在函数体内通过this来获得对它的引用),另一个是参数数组。其中,第二个参数可以是Array的实例,也可以是arguments对象
*/
function sum(num1, num2){
    return num1 + num2;
}
//因为运行函数的作用域是全局作用域,所以this代表的是window对象
function callSum1(num1, num2){
    return sum.apply(this, arguments);
}
function callSum2(num1, num2){
    return sum.apply(this, [num1, num2]);
}
console.log(callSum1(10,10));//20
console.log(callSum2(10,10));//20


/*
 call()方法与apply()方法的作用相同,它们的区别仅仅在于接收参数的方式不同。对于call()方法而言,
 第一个参数是this值没有变化,变化的是其余参数都直接传递给函数。换句话说,在使用call()方法时,传递给函数的参数必须逐个列举出来
*/

function sum(num1, num2){
    return num1 + num2;
}
function callSum(num1, num2){
    return sum.call(this, num1, num2);
}
console.log(callSum(10,10));   //20


/*
至于是使用apply()还是call(),完全取决于采取哪种函数传递参数的方式最方便。如果打算直接传入arguments对象,
或者包含函数中先接收到的也是一个数组,那么使用apply()肯定更方便;否则,选择call()可能更合适

在非严格模式下,使用函数的call()或apply()方法时,null或undefined值会被转换为全局对象。
而在严格模式下,函数的this值始终是指定的值
*/

var color = 'red';
function displayColor(){
    console.log(this.color);
}
displayColor.call(null);//red
displayColor.call(undefined);//red
displayColor.call(this);//red
displayColor.call();//red



var color = 'red';
function displayColor(){
    'use strict';
    console.log(this.color);
}
displayColor.call(null);//TypeError: Cannot read property 'color' of null



/*
应用

javascript不提供找出数组最大元素的函数。结合使用apply方法和Math.max方法,就可以返回数组的最大元素
*/
// 1.找出数组的最大元素
var a = [10, 2, 4, 15, 9];
console.log(Math.max.apply(null, a));//15

//2.将类数数组转换成真正的数据
console.log(Array.prototype.slice.apply({0:1,length:1}));//[1]


// 3.将一个数组的值push到另一个数组中
var a = [];
Array.prototype.push.apply(a,[1,2,3]);
console.log(a);//[1,2,3]
Array.prototype.push.apply(a,[2,3,4]);
console.log(a);//[1,2,3,2,3,4]

/*4.绑定回调函数的对象    由于apply方法(或者call方法)不仅绑定函数执行时所在的对象,还会立即执行函数,因此不得不把绑定语句写在一个函数体内
<body>
    <button id="btn">按钮</button>
</body>
*/

var o = {};
o.f = function() {
    console.log(this === o);   //true
};
$('#btn').on('click',function(){
    o.f.apply(o);
});





/*
bind()
​bind()是es5新增的方法,这个方法的主要作用就是将函数绑定到某个对象
​当在函数f()上调用bind()方法并传入一个对象o作为参数,这个方法将返回一个新的函数。
以函数调用的方式调用新的函数将会把原始的函数f()当做o的方法来调用,传入新函数的任何实参都讲传入原始函数
*/

function f(y){
    return this.x + y;//这个是待绑定的函数
}
var o = {x : 1};//将要绑定的对象
var g = f.bind({x:1},2);//通过调用g(x)来调用o.f(x);
console.log(g(2));//3;


// bind()方法不仅是将函数绑定到一个对象,它还附带一些其他应用:除了第一个实参之外,
// 传入bind()的实参也会绑定到this,这个附带的应用是一种常见的函数式编程技术,有时也被称为’柯里化’(currying)

var sum = function(x,y){
    return x+y;
};
var succ = sum.bind(null,1);
console.log(succ(2)); //3,x绑定到1,并传入2作为实参y



function f(y,z){
    return this.x + y + z;
}
var g = f.bind({x:1},2);
g(3); //6,this.x绑定到1,y绑定到2,z绑定到3



//使用bind()方法实现柯里化可以对函数参数进行拆分
function getConfig(colors,size,otherOptions){
    console.log(colors,size,otherOptions);
}
var defaultConfig = getConfig.bind(null,'#c00','1024*768');
defaultConfig('123');//'#c00 1024*768 123'
defaultConfig('456');//'#c00 1024*768 456'


</script>
函数的属性和方法
// 1、伪数组 arguments arguments代表的是实参。arguments只在函数中使用。
fn(2,4);
fn(2,4,6);
fn(2,4,6,8);
function fn(a,b,c) {
    console.log(arguments);
    console.log(fn.length);         //获取形参的个数  一直是3
    console.log(arguments.length);  //获取实参的个数
    console.log("----------------");
}
// 2、arguments是伪数组,是因为:arguments可以修改元素,但不能改变数组的长短。
function fn2(a,b) {
    arguments[0] = 99;  //将实参的第一个数改为99
    //arguments.push(8);  //报错,因为无法增加元素  TypeError: arguments.push is not a function
}
 
//arguments的用处1:
function nxAdd(){
    var result=0;
    for (var num in arguments){
        result+=arguments[num]
    }
    alert(result);
}
nxAdd(1,2,3,4,5);
 
//arguments的用处2:
function f(a,b,c){
    if(arguments.length!=3){
        throw new Error("function f called with "+arguments.length+" arguments,but it just need 3 arguments")
    }
    else {
        alert("success!")
    }
}
f(1,2,3,4,5);
arguments
<script>
// 匿名函数方式,多和其他函数配合使用,后面我们就会用到了
var sum = function(a, b){  //在es6中,使用var,可能会飘黄,是因为在es6中,建议你使用let来定义变量,不过不影响你使用
  return a + b;
};
sum(1, 2);
</script>
匿名函数
<script>
// 匿名函数的应用------立即执行函数,页面加载到这里,这个函数就直接执行了,不需要被调用执行

(function(){
    alert("tony");
})()

(function(arg){
    console.log(arg);
})('123')

//python中写可以这么写:ret=(lambda x,y:x+y)(10,20) 然后print(ret)

</script>
自执行函数--匿名函数的应用
<script>
/*
局部变量:
在JavaScript函数内部声明的变量(使用 var)是局部变量,所以只能在函数内部访问它(该变量的作用域是函数内部)。只要函数运行完毕,本地变量就会被删除。

全局变量:
在函数外声明的变量是全局变量,网页上的所有脚本和函数都能访问它。

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

*****作用域关系是在函数定义阶段就已经固定的,与函数的调用位置无关*****
作用域---首先在函数内部查找变量,找不到则到外层函数查找,逐步找到最外层。
*/

var city = "BeiJing";
function f() {
  var city = "ShangHai";
  function inner(){
    var city = "ShenZhen";
    console.log(city);
  }
  inner();
}

f();  //ShenZhen

//==========================================

var city = "BeiJing";
function Bar() {
  console.log(city);
}
function f() {
  var city = "ShangHai";
  return Bar;
}
var ret = f();
ret();  //BeiJing

//=========================================
var city = "BeiJing";
function f(){
    var city = "ShangHai";
    function inner(){
        console.log(city);
    }
    return inner;
}
var ret = f();
ret();   //ShangHai

//=========================================
var a=111;
function fun() {
    console.log(a)  //访问全局变量  111
}
fun();

//=========================================
function fun() {
    b=222;    //如果不用var那么它是全局变量
}
console.log(b);  //函数外面可以访问222

//=========================================
function fun() {
    var c=333;    //函数内的变量
}
console.log(c)  //报错 c is not defined

</script>
js作用域
#!/usr/bin/env python
# -*- coding: utf-8 -*-

#*****作用域关系是在函数定义阶段就已经固定的,与函数的调用位置无关*****
#######################################
city = "BeiJing"
def f():
    city = "ShangHai"
    def inner():
        city = "ShenZhen"
        print(city)
    inner()
f() #ShenZhen

#######################################
city = "BeiJing"
def Bar():
    print(city)
def f():
    city = "ShangHai"
    return Bar

ret = f()
ret()   #BeiJing

#######################################
city = "BeiJing"
def f():
    city = "ShangHai"
    def inner():
        print(city)
    return inner

ret = f()
ret()  #ShangHai
python作用域
<script type="text/javascript">

/*
闭包:
javascript有两种作用域:全局作用域和函数作用域。
*/

var a = 123;
function fn(){
    console.log(a);
}
fn();// 123


//函数fn1可以读取全局变量a。 但是,函数外部无法读取函数内部声明的变量
function fn(){
    var a = 123;
}
console.log(a); //Uncaught ReferenceError: a is not defined

/*
可以把闭包简单理解为:定义在一个函数内部的函数。
闭包最大的特点:就是它可以记住诞生的环境,比如fn2记住了它诞生的环境fn1,所以在fn2可以得到fn1的内部变量。
本质上,闭包就是函数内部和函数外部链接的一座桥梁

闭包就是函数fn2,既能够读取其它函数内部变量的函数。  (需要得到函数内的局部变量。)
*/

function fn1(){
    var a = 123;
    function fn2(){
        console.log(a); //123
    }
    return fn2;
}
var result = fn1();
console.log(result()); //123




/*
闭包的用途
【1】读取函数内部的变量,让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在。
例子:闭包使得内部变量记住上一次调用时的运算结果
*/

function a() {
    var start = 5;
    function b() {
        start++;
        return start;
    }
    return b;
}
var inc = a();
console.log(inc());// 6
console.log(inc());// 7
console.log(inc());// 8

console.log(a()());// 6
console.log(a()());// 6
console.log(a()());// 6

//释放内存
inc = null;
console.log(inc)  //null
/*
上面代码中,start是函数a的内部变量。通过闭包(函数a的内部函数b被函数a外的一个变量引用的时候,就创建了一个闭包),
start的状态被保留了,每一次调用都是在上一次调用的基础上进行计算。从中可以看出,闭包inc使得函数a的内部环境一直存在。
所以,闭包可以看作是函数内部作用域的一个接口。

为什么会这样呢?原因就在于inc始终在内存中,而inc的存在依赖与a,因此也始终在内存中,不会在调用结束后,被垃圾回收机制回收。

在Javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收。如果两个对象互相引用,而不再被第3者所引用,
那么这两个互相引用的对象也会被回收。因为函数a被b引用,b又被a外的inc引用,这就是为什么函数a执行后不会被回收的原因。
*/



//【2】封装对象的私有属性和私有方法
function Person(name){
    var _age;
    function setAge(n){
        _age = n;
    }
    function getAge(){
        return _age;
    }
    return {
        name:name,
        getAge:getAge,
        setAge:setAge
    }
}
var p1 = Person('tom');
p1.setAge(18);
console.log(p1.getAge());//18


/*
上面代码中,函数Person的内部变量_age,通过闭包getAge和setAge,变成了返回对象p1的私有变量。
注意,外层函数每次运行完,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,所以消耗很大。因此不能滥用闭包,否则会造成网页的性能问题。

使用闭包的注意点:
由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。
解决方法是,在退出函数之前,将不使用的局部变量全部删除。
闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),
把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
https://www.jianshu.com/p/26c81fde22fb


闭包需要满足三个条件:
【1】访问所在作用域【2】函数嵌套 【3】在所在作用域外被调用
*/


/* python中闭包
#### 创建闭包函数必须满足3点:
    # 1、必须有一个内嵌函数
    # 2、内嵌函数必须引用外部函数中的变量(非全局作用域的引用)
    # 3、外部函数的返回值必须是内嵌函数

def a():
    start=5
    def b():
        nonlocal start
        start+=1  #python 中没有++操作
        return start
    return b

inc=a()
print(inc())  #6
print(inc())  #7
print(inc())  #8

print(a()())  #6
print(a()())  #6
print(a()())  #6

inc=None
print(inc)
*/



/*
立即执行函数:

实现:

在 Javascript 中,圆括号()是一种运算符,跟在函数名之后,表示调用该函数。比如,fn()就表示调用fn函数。
但有时需要定义函数之后,立即调用该函数。这种函数就叫做立即执行函数,全称为立即调用的函数表达式IIFE(Imdiately Invoked Function Expression)

注意:javascript引擎规定,如果function关键字出现在行首,一律解释成函数声明语句
函数声明语句需要一个函数名,由于没有函数名,所以报错

function (){}();
//Uncaught SyntaxError: Unexpected token (

解决方法就是不要让function出现在行首,让引擎将其理解成一个表达式。最简单的处理,就是将其放在一个圆括号里面

常用的两种写法
(function(){console.log(1)}());
//或者
(function(){console.log(1)})();
上面两种写法都是以圆括号开头,引擎就会认为后面跟的是一个表示式,而不是函数定义语句,所以就避免了错误。

注意:上面两种写法最后的分号都是必须的。如果省略分号,遇到连着两个 IIFE,可能就会报错。
// 报错
(function(){ console.log(1); }())
(function(){ console.log(1); }())

上面代码的两行之间没有分号,JavaScript 会将它们连在一起解释,将第二行解释为第一行的参数。

其它写法:
推而广之,任何让解释器以表达式来处理函数定义的方法,都能产生同样的效果,比如下面三种写法。

var i = function(){console.log(1)}();
true && function (){console.log(1)}();
0,function(){console.log(1)}();
!function(){console.log(1) }();
~function(){ console.log(1)}();
-function(){ console.log(1)}();
+function(){ console.log(1)}();
new function(){ console.log(1)};
new function(){console.log(1)}();
*/




/*
用途:

​ IIFE一般用于构造私有变量,避免全局污染。

【1】全局变量
一般情况下,我们会使用全局变量来保存该数字状态

var a = 0;
function add(){
    return ++a;
}
add();
console.log(a);//1
add();
console.log(a);//2

变量a实际上只和add函数相关,却声明为全局变量,不太合适。


【2】自定义属性
​ 将变量a更改为函数的自定义属性更为恰当

function add(){
    return ++add.count;
}
add.count = 0;
console.log(add());//1
console.log(add());//2

有些代码可能会无意中将add.count重置


【3】IIFE
使用IIFE把计数器变量保存为私有变量更安全,同时也可以减少对全局空间的污染

var add = (function (){
    var counter = 0;
    return function (){
        return ++counter;
    }
})();

console.log(add());  //1
console.log(add());  //2
console.log(add());  //3



注意事项

执行如下代码会报错,提示此时的a是undefined
var a = function(){
    return 1;
}
(function(){
    console.log(a());//报错
})();


这是因为没有加分号,浏览器将上面代码解释成如下所示
var a = function(){
    return 1;
}(function(){
    console.log(a());//报错
})();


如果加上分号,就不会出错了
var a = function(){
    return 1;
};
(function(){
    console.log(a());//1
})()

*/




/*

对循环和闭包的错误理解

容易犯错的一件事

function foo(){
    var arr = [];
    for(var i = 0; i < 2; i++){
        arr[i] = function (){
            return i;
        }
    }
    return arr;
}
var bar = foo();
console.log(bar[0]());//2

犯错原因是在循环的过程中,并没有把函数的返回值赋值给数组元素,而仅仅是把函数赋值给了数组元素。
这就使得在调用匿名函数时,通过作用域找到的执行环境中储存的变量的值已经不是循环时的瞬时索引值,而是循环执行完毕之后的索引值

IIFE解决容易犯错的问题

可以利用IIF传参和闭包来创建多个执行环境来保存循环时各个状态的索引值。因为函数传参是按值传递的,不同的参数的函数被调用时,会创建不同的执行环境

function foo() {
    var arr = [];
    for (var i = 0; i < 2; i++) {
        arr[i] = (function(j) {
            return function (){
                return j;
            };
        })(i);
    }
    return arr;
}
var bar = foo();
console.log(bar[1]()); //1

或者

function foo() {
    var arr = [];
    for (var i = 0; i < 2; i++) {
        (function(i) {
            arr[i] = function() {
                return i;
            }
        })(i)
    }
    return arr;
}
var bar = foo();
console.log(bar[1]());



块作用域:

使用IIFE还是较为复杂,使用块作用域则更为方便
由于块作用域可以将索引值i重新绑定到了循环的每一个迭代中,确保使用上一个循环迭代结束时的值重新进行赋值,相当于为每一次索引值都创建一个执行环境

function foo(){
    var arr = [];
    for(let i = 0; i < 2; i++){
        arr[i] = function(){
            return i;
        }
    }
    return arr;
}
var bar = foo();
console.log(bar[1]());//0
*/





/*

闭包的的常见形式:

根据闭包的定义,我们知道,无论何种手段,只要将内部函数传递到所在的作用域以外,它都会持有对原始作用域的引用,
无论在何处执行这个函数都会使用闭包,接下来,将详细介绍闭包的形式


返回值:  最常用的一种形式是函数作为返回值被返回
var fn = function(){
    var a = 'tom';
    var b = function(){
        return a;
    };
    return b;
};
console.log(fn()());  //tom



函数赋值: 一种变形的形式是将内部函数赋值给一个外部变量
var fn2;
var fn = function(){
    var a = 'tom';
    var b = function(){
        return a;
    };
    fn2 = b;
};

fn();
console.log(fn2());



函数参数:闭包可以通过函数参数传递函数形式来实现
var fn2 = function(fun){
    console.log(fun());
};
var fn = function(){
    var a = 'tom';
    var b = function(){
        return a;
    };
    fn2(b);
};
fn();



IIFE:
由前面的示例代码可知,函数fn()都是声明后立即被调用的,因此可以使用IIFE来替代。但是,要注意的是,这里的fn2()只能使用函数声明语句的形式,而不能使用函数表达式。
function fn2(fn){
    console.log(fn());
}
(function(){
    var a = 'tom';
    var b = function(){
        return a;
    }
    fn2(b);
})();



循环赋值
在闭包问题上,最常见的一个错误就是循环赋值的错误
function foo(){
    var arr = [];
    for(var i = 0; i < 5; i++){
        arr[i] = function(){
           return i;
        }
    }
    return arr;
}
var bar = foo();
console.log(bar[0]());//6


正确的写法如下:
function foo(){
    var arr = [];
    for(var i = 0; i< 5; i++){
        arr[i] = (function(j){
            return function test(){
                return j;
            }
        })(i)
    }
    return arr;
}
var bar = foo();
console.log(bar[0]());//0



getter和setter:
我们通过提供getter()和setter()函数来将要操作的变量保存在函数内部,防止其暴露在外部

var getValue,setValue;
(function(){
    var secret = 0;
    getValue = function(){
        return secret;
    }
    setValue = function(v){
        if(typeof v === 'number'){
            secret = v;
        }
    }
})();
console.log(getValue());//0
setValue(1);
console.log(getValue());//1




迭代器
我们经常使用闭包来实现一个累加器

var add = (function(){
    var counter = 0;
    return function (){
        return ++counter;
    }
})();
console.log(add());//1
console.log(add());//2


类似地,使用闭包可以很方便的实现一个迭代器
function setup(x){
    var i = 0;
    return function (){
        return x[i++];
    }
}
var next = setup(['a','b','c']);
console.log(next());//'a'
console.log(next());//'b'
console.log(next());//'c'



区分首次:
var firstLoad = (function(){
  var _list = [];
  return function(id){
    if(_list.indexOf(id) >= 0){
      return false;
    }else{
      _list.push(id);
      return true;
    }
  }
})();
console.log(firstLoad(10));//true
console.log(firstLoad(10));//false
console.log(firstLoad(20));//true
console.log(firstLoad(20));//false



缓存机制
通过闭包加入缓存机制,使得相同的参数不用重复计算,来提高函数的性能
未加入缓存机制前的代码如下

var mult = function (){
    var a = 1;
    for(var i = 0; i < arguments.length; i++){
        a = a * arguments[i];
    }
    return a;
}
console.log(mult(1,1,1,2,3,3));//18


加入缓存机制后,代码如下:

var mult = function(){
  var cache = {};
  var calculate = function(){
    var a = 1;
    for(var i = 0,len = arguments.length; i<len; i++){
      a = a * arguments[i];
    }
    return a;
  };
  return function(){
    var args = Array.prototype.join.call(arguments,',');
    if(args in cache){
      return cache[args];
    }
    return cache[args] = calculate.apply(null,arguments);
  }
}()
console.log(mult(1,1,1,2,3,3));//18



img对象
img对象经常用于数据上报
var report = function (src){
    var img = new Image();
    img.src = src;
}
report('http://xx.com/getUserInfo');

但是,在一些低版本的浏览器中,使用report函数进行数据上报会丢失30%左右的数据,也就是说,report函数并不是每一次都成功地发起了HTTP请求

原因是img是report函数中的局部作用域,当report函数的调用结束后,img局部变量随即被销毁,而此时或许还没来得及发出HTTP请求,所以此次请求就会丢失掉

​现在把img变量用闭包封存起来,就能解决请求丢失的问题

var report = (function(){
   var imgs = [];
    return function(src){
        var img = new Image();
        imgs.push(img);
        img.src = src;
    }
})()
report('http://xx.com/getUserInfo');


*/

</script>
闭包
<script type="text/javascript">

/*
this绑定规则


默认绑定: 全局环境下,this默认绑定到window
console.log(this === window); //true


函数独立调用时:this默认绑定到window
function foo(){
    console.log(this === window);
}
foo();//true


被嵌套的函数独立调用时:this默认绑定到window
var a = 0;
var obj = {
    a : 2,
    foo : function (){
        function test(){
            console.log(this);  //window
        }
        test();
    }
};
obj.foo();
上面代码虽然test()函数被嵌套在obj.foo()函数中,但test()函数是独立调用,而不是方法调用。所以this默认绑定到window



IIFE: IIFE立即执行函数实际是函数声明后立即调用执行,内部的this指向了window
var a = 0;
function foo(){
    (function test(){
        console.log(this);  //window
        console.log(this.a);
    })()
};
var obj = {
    a : 2,
    foo:foo
};
obj.foo();//0

等价于上例
var a = 0;
var obj = {
    a : 2,
    foo : function(){
        function test(){
            console.log(this.a);
        }
        test();
    }
}
obj.foo();//0





闭包
类似地,test()函数是独立调用,而不是方法调用,所以this默认绑定到window

注意:函数共有4中调用方法
var a = 0;
function foo() {
    function test() {
        console.log(this.a);
    }
    test();
}
var obj = {
    a: 2;
    foo: foo
}
obj.foo();//0


由于闭包的this默认绑定到window对象,但又常常需要访问嵌套函数的this,所以常常在嵌套函数中使用var that = this,
然后在闭包中使用that替代this,使用作用域查找的方法来找到嵌套函数的this值

var a = 0;
function foo(){
    var that = this;
    function test(){
        console.log(that.a);
    }
    return test;
};
var obj = {
    a : 2,
    foo:foo
}
obj.foo()();//2




隐式绑定:
一般地,被直接对象所包含的函数调用,也被称为方法地调用,this隐式绑定到该直接对象

function foo(){
    console.log(this.a);
}
var obj1 = {
    a : 1,
    foo: foo,
    obj2 : {
        a:2,
        foo:foo
    }
}
//foo()函数的直接对象是obj1,this隐式绑定到obj1
obj1.foo();//1
//foo()函数的直接对象是obj2,this隐式绑定到obj2
obj1.obj2.foo();//2





隐式丢失:
隐式丢失是指被隐式绑定的函数丢失绑定对象,从而默认绑定到window。这种情况容易出错却又常见

【函数别名】
var a = 0;
function foo(){
    console.log(this.a);
}
var obj = {
    a: 1,
    foo:foo
}
//把obj.foo赋予别名bar,造成了隐式丢失,因为只是把foo()函数赋给了bar,而bar与obj对象则毫无关系
var bar = obj.foo;
bar();//0


//等价于
var a = 0;
var bar = function foo(){
    console.log(this.a);
}
bar();//0


【参数传递】

var a = 0;
function foo() {
    console.log(this.a);
}
function bar(fn) {
    fn();
}
var obj = {
    a: 2,
    foo: foo
}
//把obj.foo当做参数传递给bar函数时,有隐式的函数赋值 fn = obj.foo,只是把foo函数赋给了fn,而fn与obj对象毫无关系
bar(obj.foo);//0

//等价于
var a = 0;
function bar(fn){
    fn();
}
bar(function foo(){
    console.log(this.a);
})




【内置函数】

内置函数与上例类似,也会造成隐式丢失

var a = 0;
function foo(){
    console.log(this.a);
}
var obj = {
    a : 2,
    foo:foo
}
setTimeout(obj.foo,100);//0


【间接调用】
函数的“间接引用”一般都在无意间创建,最容易在赋值时发生,会造成隐式丢失

function foo(){
    console.log(this.a);
}
var a = 2;
var o = {a: 3,foo: foo};
var p = {a: 4};
o.foo();//3;
//将o.foo函数赋值给p.foo函数,然后立即执行。相当于仅仅是foo()函数的立即调用
(p.foo = o.foo)();//2
//另一种情况
function foo() {
    console.log( this.a );
}
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo(); // 3
//将o.foo函数赋值给p.foo函数,之后p.foo函数再执行,是属于p对象的foo函数的执行
p.foo = o.foo;
p.foo();//4



【其他情况】
​ 在javascript引擎内部,obj和obj.foo储存在两个内存地址,简称为M1和M2。只有obj.foo()这样调用时,是从M1调用M2,因此this指向obj。但是,下面三种情况,都是直接取出M2进行运算,然后就在全局环境执行运算结果(还是M2),因此this指向全局环境

var a = 0;
var obj = {
    a:2,
    foo:foo
}
function foo(){
    console.log(this.a);
}
(obj.foo = obj.foo)();//0
(false || obj.foo)();//0
(1,obj.foo)();//0




显示绑定
通过call()、apply()、bind()方法把对象绑定到this上,叫做显示绑定。对于被调用的函数来说,叫做间接调用

var a = 0;
function foo(){
    console.log(this.a);
}
var obj = {
    a : 2
};
foo();//0
foo.call(obj);//2


普通的显示绑定无法解决隐式丢失问题

var a = 0;
function foo(){
    console.log(this.a);
}
var obj1 = {
    a : 1
};
var obj2 = {
    a : 2
}
foo.call(obj1);//1
foo.call(obj2);//2


【硬绑定】
​ 硬绑定是显式绑定的一个变种,使this不能再被修改

var a = 0;
function foo(){
    console.log(this.a);
}
var obj = {
    a:2
};
var bar = function (){
    foo.call(obj);
}
//在bar函数内部手动调用foo.call(obj)。因此,无论之后如何调用函数bar,它总会手动会在obj上调用foo
bar();//2
setTimeout(bar,2000);//2
bar.call(window);//2



【API】
​ javascript中新增了许多内置函数,具有显式绑定的功能,如数组的5个迭代方法:map()、forEach()、filter()、some()、every()

var id = 'window';
function foo(el){
    console.log(el,this.id);
}
var obj = {
    id: 'fn'
};
[1,2,3].forEach(foo);//1 "window" 2 "window" 3 "window"
[1,2,3].forEach(foo,obj);//1 "fn" 2 "fn" 3 "fn"



new绑定
如果函数或者方法调用之前带有关键字new,它就构成构造函数调用。对于this绑定来说,称为new绑定

【1】构造函数通常不适用return关键字,他们通常初始化新对象,当构造函数的函数体执行完毕时,它会显式返回。在这种情况下,构造函数调用表达式的计算结果就是这个新对象的值
function fn(){
    this.a = 2;
}
var test = new fn();
console.log(test);//{a:2}



【2】如果构造函数使用return语句但没有指定返回值,或者返回一个原始值,那么这时将忽略返回值,同时使用这个新对象作为调用结果
function fn(){
    this.a = 2;
    return;
}
var test = new fn();
console.log(test);//{a:2}


【3】使用构造函数显示地使用return语句返回一个对象,那么调用表达式的值就是这个对象
var obj = {a:1};
function fn(){
    this.a = 2;
    return obj;
}
var test = new fn();
console.log(test);//{a:1}
注意:尽管有时候构造函数看起来像一个方法调用,它依然会使用这个新对象作为this。也就是说,在表达式new o.m()中,this并不是o
var o = {
    m:function(){
        return this;
    }
}
var obj = new o.m();
console.log(obj,obj === o);//{} false
console.log(obj.contructor === o.m);//true



严格模式
【1】严格模式下,独立调用的函数的this指向undefined

function fn(){
    'use strict';
    console.log(this);//undefined
}
fn();
function fn(){
    console.log(this);//window
}
fn();

【2】在非严格模式下,使用函数的call()或apply()方法时,null或undefined值会被转换成全局对象。而在严格模式下,函数的this值始终是指定的值
var color = 'red';
function displayColor(){
    console.log(this.color);
}
displayColor.call(null);//red
var color = 'red';
function displayColor(){
    'use strict';
    console.log(this.color);
}
displayColor.call(null);//TypeError: Cannot read property 'color' of null



总结

​ this的四种绑定规则:默认绑定、隐式绑定、显式绑定和new绑定,分别对应函数的四种调用方式:独立调用、方法调用、间接调用和构造函数调用。

​ 分清这四种绑定规则不算难,但是比较麻烦的是需要练就火眼金睛,识别出隐式丢失的情况。

​ 说到底,JavaScript如此复杂的原因是因为函数过于强大。因为,函数是对象,所以原型链比较复杂;因为函数可以作为值被传递,所以执行环境栈比较复杂;同样地,因为函数具有多种调用方式,所以this的绑定规则也比较复杂

​ 只有理解了函数,才算理解javascript
*/

</script>
this指向

六、Object

  • 使用Object或对象字面量创建对象【代码大量重复】
  • 工厂模式创建对象【用instanceof检测都是Object类型】
  • 构造函数模式创建对象【相同的方法过多】
  • 原型模式创建对象
<script>

//JS中最基本创建对象的方式:使用Object创建对象
var student = new Object();
student.name = "easy";
student.age = "20";
//给对象添加fav的方法
student.fav = function(){
    console.log('coding');
};
//这样,一个student对象就创建完毕,拥有2个属性name以及age,分别赋值为"easy"和20。和一个方法fav
//如果你嫌这种方法有一种封装性不良的感觉。来一个对象字面量方式创建对象。

//使用字面量方式
var student0 = {
  name : "easy",
  age : 20,
  fav : function(){
        console.log('coding');
    }
};

//属性名也可以使用字符串,如下:
var studentt = {
    "name" : 'easy',
    "age": 20,
    "fav": function(){
        console.log('coding');
    }
};


var student1 = {
  name : "easy1",
  age : 20
};

var student2 = {
  name : "easy2",
  age : 20
};

var studentn = {
  name : "easyn",
  age : 20
};

// 要创建同类的student1,student2,…,studentn时,不得不将以上的代码重复n次....
// 有个问题?能不能像工厂车间那样,有一个车床就不断生产出对象呢? 故工厂模式登录

// var sutdent = {};//与new Object()相同
// 点语法student.name   括号表示法student['name']   推荐使用点语法来访问对象的属性。
</script>
使用Object或对象字面量创建对象
<script>
// 2.工厂模式创建对象
//JS中没有类的概念,那么我们不妨就使用一种函数将以上对象创建过程封装起来以便于重复调用,同时可以给出特定接口来初始化对象
function createStudent(name, age) {
  var obj = new Object();
  obj.name = name;
  obj.age = age;
  return obj;
}
var student1 = createStudent("easy1", 20);
var student2 = createStudent("easy2", 20);


function createFruit(name, color) {
  var obj = new Object();
  obj.name = name;
  obj.color = color;
  return obj;
}
var v1 = createStudent("easy1", 20);
var v2 = createFruit("apple", "green");
console.log(v1 instanceof Object); //true
console.log(v2 instanceof Object); //true
console.log(v1 instanceof createFruit); //false
console.log(v2 instanceof createFruit); //false

/*
对象v1、v2,用instanceof操作符去检测,他们统统都是Object类型。
我们希望v1是Student类型的,而v2是Fruit类型的。为了实现这个目标,可以用自定义构造函数的方法来创建对象
*/
</script>
工厂模式创建对象
<script>
/*
构造函数和普通函数有什么区别:
1、对于任意函数,使用new操作符调用,那么它就是构造函数;不使用new操作符调用,那么它就是普通函数。
2、按照惯例,约定构造函数名以大写字母开头,普通函数以小写字母开头,这样有利于显性区分二者。
3、使用new操作符调用构造函数时,会经历4个阶段。
    (1)创建一个新对象;
    (2)将构造函数作用域赋给新对象(使this指向该新对象);
    (3)执行构造函数代码;
    (4)返回新对象;
*/
// 3.构造函数模式创建对象

function Student(name, age) {
  this.name = name;
  this.age = age;
  this.alertName = function(){
    alert(this.name)
  };
}

function Fruit(name, color) {
  this.name = name;
  this.color = color;
  this.alertName = function(){
    alert(this.name)
  };
}

var v1 = new Student("easy", 20);
var v2 = new Fruit("apple", "green");

console.log('=============================');
console.log(v1 instanceof Student);  //true
console.log(v2 instanceof Student); //false
console.log(v1 instanceof Fruit);  //false
console.log(v2 instanceof Fruit);  //true

console.log(v1 instanceof Object);  //true 任何对象均继承自Object
console.log(v2 instanceof Object);  //true 任何对象均继承自Object

//可以区分对象类型了,但是Student和Fruit对象中有同样的方法,调用时无疑是对内存的消耗。
// 3.1 过度  将alertName()函数定义为全局函数,解决了内存浪费的问题,
// 如果这样定义的全局函数多了,想要将自定义对象封装的初衷便几乎无法实现了。更好的方案是通过原型对象模式来解决。
/*
function Student(name, age) {
  this.name = name;
  this.age = age;
  this.alertName = alertName;
}

function alertName() {
  alert(this.name);
}

var stu1 = new Student("easy1", 20);
var stu2 = new Student("easy2", 20);

stu1.alertName();  //在调用stu1.alertName()时,this对象才被绑定到stu1上。
*/
</script>
构造函数模式创建对象
<script>
// 4.原型模式创建对象 ******************
function Student() {
    this.name = 'easy';
    this.age = 20;
}

Student.prototype.alertName = function(){
    alert(this.name);
};

var stu1 = new Student();
var stu2 = new Student();

stu1.alertName();
stu2.alertName();

alert(stu1.alertName == stu2.alertName);  //true 二者共享同一函数

</script>
原型的模式创建对象

七、Globle对象

  • Global(全局)对象可以说是 ECMAScript 中最特别的一个对象了,因为不管你从什么角度上看, 这个对象都是不存在的。
  • ECMAScript 中的 Global 对象在某种意义上是作为一个终极的“兜底儿对象” 来定义的。换句话说,不属于任何其他对象的属性和方法,最终都是它的属性和方法。
  • 事实上,没有全局变量或全局函数; 所有在全局作用域中定义的属性和函数,都是 Global 对象的属性。诸如 isNaN()、isFinite()、parseInt()以及 parseFloat(),encodeURI()和 encodeURIComponent() 等实际上全都是 Global 对象的方法。
  • Global 对象的 encodeURI()和 encodeURIComponent()方法可以对 URI(Uniform Resource Identifiers,通用资源标识符)进行编码,以便发送给浏览器。有效的 URI 中不能包含某些字符,例如:空格。而这两个 URI 编码方法就可以对 URI 进行编码,它们用特殊的 UTF-8 编码替换所有无效的字符, 从而让浏览器能够接受和理解。
  • encodeURI()不会对本身属于 URI 的特殊字符进行编码,例如冒号、正斜杠、 问号和井字号
  • encodeURIComponent()则会对它发现的任何非标准字符进行编码
  • 一般来说,使用 encodeURIComponent()方法的时候要比使用 encodeURI()更多,因为在实践中更常见的是对查询字符串参数而不是对基础 URI 进行编码。
  • decodeURI()只能对使用 encodeURI()替换的字符进行解码,同样地decodeURIComponent()能够解码使用 encodeURIComponent()编码的
  • console.log(encodeURI('http://www.baidu/web index.html')):http://www.baidu/web%20index.html
  • console.log(encodeURIComponent('http://www.baidu/web index.html')):http%3A%2F%2Fwww.baidu%2Fweb%20index.html
  • 使用 encodeURI()编码后的结果是除了空格之外的其他字符都原封不动,只有空格被替换成了 %20。而 encodeURIComponent()方法则会使用对应的编码替换所有非字母数字字符

八、window对象

  • ECMAScript 虽然没有指出如何直接访问 Global 对象,但 Web 浏览器都是将这个全局对象作为 window 对象的一部分加以实现的。因此,在全局作用域中声明的所有变量和函数,就都成为了 window 对象的属性。
var color = "red";
function sayColor(){
    alert(window.color);
}
window.sayColor();  //"red"
View Code

JSON

一、两种结构

  • JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,采用完全独立于语言的文本格式,是理想的数据交换格式。
  • JSON是 JavaScript 原生格式,这意味着在 JavaScript 中处理 JSON数据不须要任何特殊的 API 或工具包。
  • JSON 是 JS 对象的字符串表示法,它使用文本表示一个 JS 对象的信息,本质是一个字符串。
  • 在JSON中,有两种结构:对象和数组。

    1. 对象
      var packJSON= {"name":"alex", "password":"123"};
      一个对象以“{”开始,“}”结束,“key/value”之间运用 “,”分隔。
    2. 数组
      var packJSON = [{"name":"alex", "password":"123"}, {"name":"wusir", "password":"456"}];
      数组是值的有序集合。一个数组以“[”开始,“]”结束。值之间运用 “,”分隔。

二、JSON对象和JSON字符串转换

在数据传输过程中,JSON是以字符串的形式传递的,而JS操作的是JSON对象

  • JSON字符串
    var jsonStr ='{"name":"alex", "password":"123"}' ;
  • JSON对象:
    var jsonObj = {"name":"alex", "password":"123"};
  • JSON字符串转换JSON对象:
    var jsonObject= JSON.parse(jsonstr);
  • JSON对象转化JSON字符串:
    var jsonstr =JSON.stringify(jsonObject );

三、遍历JSON对象和JSON数组

<script type="text/javascript">
//对象
var packAlex  = {"name":"alex", "password":"123"} ;
for(var k in packAlex ){//遍历packAlex 对象的每个key/value对,k为key
   console.log(k + " " + packAlex[k]);
}
//数组
var packAlex = [{"name":"alex", "password":"123"}, {"name":"wusir", "password":"456"}];
for(var i in packAlex){//遍历packJson 数组时,i为索引
   console.log(packAlex[i].name + " " + packAlex[i].password);
}
</script>
遍历JSON对象和JSON数组

运算符

  • 算术运算中:'+': 字符串可相加,数字也可相加,字符串和数字也可以相加。值得注意的是,如果字符串和数字相加会自动把结果转换成字符串。
  • 算术运算中:'-':字符串 - 数值 = 数值(字符串与数字之间相减的结果是数字)
  • 算术运算中:js中没有整除// 注意区分python中的整除//
  • 算术运算中:js中有++自增,--自减,python中没有这样的写法
  • 逻辑运算中:&& 逻辑与 ||逻辑或 !逻辑非 注意区分python中的 and or not
  • 比较运算中:===(值和类型都相等)和!==(值和类型有一个不相等或者都相等)
  • 值 null 和 undefined 相等
算术运算符:
    +   -    *    /     %       ++        -- 
比较运算符:
    >   >=   <    <=    !=    ==    ===   !==
逻辑运算符:
     &&   ||   !
赋值运算符:
    =  +=   -=  *=   /=
字符串运算符:
    +  连接,两边操作数有一个或两个是字符串就做连接运算
    
// && ||
if (2>1 && [1,2]){
    console.log("...")  //...
}

if (2<1 && [1,2]){
    console.log("...")  //
}
//
console.log(1 && 3); //3
console.log(0 && 3); //0
console.log(0 || 3); //3
console.log(2 || 3); //2

//字符串比较
var bResult = "Blue" < "alpha";
console.log(bResult); //输出 true  因为字母 B 的字符代码是 66,字母 a 的字符代码是 97。
var bResult = "25" < "3";
console.log(bResult); //输出 "true"  ("2" 的字符代码是 50,"3" 的字符代码是 51)。
var bResult = "25" < 3;
console.log(bResult); //输出 "false"  这里,字符串 "25" 将被转换成数字 25,然后与数字 3 进行比较
/*
总结:
比较运算符两侧如果一个是数字类型,一个是其他类型,会将其类型转换成数字类型.
比较运算符两侧如果都是字符串类型,比较的是最高位的asc码,如果最高位相等,继续取第二位比较.
*/

//等性运算符:执行类型转换的规则如下:

如果一个运算数是 Boolean 值,在检查相等性之前,把它转换成数字值。false 转换成 0,true 为 1。
如果一个运算数是字符串,另一个是数字,在检查相等性之前,要尝试把字符串转换成数字。
如果一个运算数是对象,另一个是字符串,在检查相等性之前,要尝试把对象转换成字符串。
如果一个运算数是对象,另一个是数字,在检查相等性之前,要尝试把对象转换成数字。
在比较时,该运算符还遵守下列规则:

值 null 和 undefined 相等。
在检查相等性时,不能把 null 和 undefined 转换成其他值。
如果某个运算数是 NaN,等号将返回 false,非等号将返回 true。
如果两个运算数都是对象,那么比较的是它们的引用值。如果两个运算数指向同一对象,那么等号返回 true,否则两个运算数不等。
View Code

流程控制

var ji  = 20;
if(ji >= 20){
    console.log('恭喜你,吃鸡成功,大吉大利')
}
alert('alex');//下面的代码还会执行
if单分支
var ji  = 20;
if(ji>=20){
    console.log('恭喜你,吃鸡成功,大吉大利')
}else{
    console.log('很遗憾 下次继续努力')
if...else...
if (true) {
   //执行操作
}else if(true){
    //满足条件执行            
}else if(true){
   //满足条件执行        
}else{
  //满足条件执行
}
if...else if...else
var gameScore = 'better';
switch(gameScore){
//case表示一个条件 满足这个条件就会走进来 遇到break跳出。如果某个条件中不写 break,那么直到该程序遇到下一个break停止
    case 'good':
    console.log('玩的很好')
    //break表示退出
    break;
    case  'better':
    console.log('玩的老牛逼了')
    break;
    case 'best':
    console.log('恭喜你 吃鸡成功')
    break;

    default:
    console.log('很遗憾')
}
case语句
var i = 1; //初始化循环变量

while(i<=9){ //判断循环条件
    console.log(i);
    i = i+1; //更新循环条件
}
while循环
//不管有没有满足while中的条件do里面的代码都会走一次
var i = 3;//初始化循环变量
do{

    console.log(i)
    i++;//更新循环条件

}while (i<10) //判断循环条件
do-while循环
for(var i = 1;i<=10;i++){
        console.log(i)
}
 
var arr = [1,2,3,4,5]
for (var n in arr){
        console.log(n)
}
for循环
var a = 1
var b =2
var c = a>b ? a:b  //如果a>b成立返回a,否则返回b
console.log(c)
三元运算符

异常处理

try {
//这段代码从上往下运行,其中任何一个语句抛出异常该代码块就结束运行
}
catch (e) {
    // 如果try代码块中抛出了异常,catch代码块中的代码就会被执行。
    //e是一个局部变量,用来指向Error对象或者其他抛出的对象
}
finally {
    //无论try中代码是否有异常抛出(甚至是try代码块中有return语句),finally代码块中始终会被执行。
}
注:主动抛出异常 throw new Error('xxxx')
异常处理

js的作用域

  • 作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。
  • 在JavaScript中,变量的作用域有全局作用域和局部作用域两种。

一、全局作用域(Global Scope)

在代码中任何地方都能访问到的对象拥有全局作用域

1、最外层函数和在最外层函数外面定义的变量拥有全局作用域

<script>
var name="alex";
    function foo(){
        var age=23;
        function inner(){
            console.log(age);
        }
        inner();
    }
    console.log(name);    // alex
    //console.log(age);   // Uncaught ReferenceError: age is not defined
    foo();                // 23
    inner();              // Uncaught ReferenceError: inner is not defined
</script>
View Code

2、所有末定义直接赋值的变量自动声明为拥有全局作用域

<script>
var name="alex";
    function foo(){
        age=23;
        var sex="male"
    }
    foo();
    console.log(age);   //  23
    console.log(sex);   // sex is not defined
</script>
View Code

3、所有window对象的属性拥有全局作用域

一般情况下,window对象的内置属性都都拥有全局作用域,例如window.alert()、window.location、window.top等等。

二、局部作用域(Local Scope)

  • 一般只在固定的代码片段内可访问到,最常见的例如函数内部,所以也称为函数作用域
  • 如示例1中的age与inner都只有局部作用域。(js中if、for没有自己的作用域)

三、作用域链(Scope Chain)

  • 在JavaScript中,函数也是对象,实际上,JavaScript里一切都是对象。
  • 函数对象和其它对象一样,拥有可以通过代码访问的属性和一系列仅供JavaScript引擎访问的内部属性。
  • 其中一个内部属性是[[Scope]],由ECMA-262标准第三版定义,该内部属性包含了函数被创建的作用域中对象的集合,
  • 这个集合被称为函数的作用域链,它决定了哪些数据能被函数访问。

1、函数定义阶段:

  • 当一个函数创建后,它的作用域链会被创建此函数的作用域中可访问的数据对象填充。
  • 在函数bar创建时,它的作用域链中会填入一个全局对象,该全局对象包含了所有全局变量,如下图所示:

2、函数调用阶段:

  • 解析到函数调用时,即bar(5),会生成一个active object的对象
  • 该对象包含了函数的所有局部变量、命名参数、参数集合以及this,然后此对象会被推入作用域链的前端,当运行期上下文被销毁,活动对象也随之销毁。
  • 新的作用域链如下图所示:

3、实例练习

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title></title>
</head>
<body>
<script>
/*
域:只要是域就会发生域解析
    1:script:全局变量,全局函数,自上而下(所以引入jQuery库的时候要在script前引入)
    2:函数:由里到外
    3:{}

"JS解析器":浏览器内部专门用来处理js程序,为了理解起的名字,至少包含下面2步骤
1:JS的域解析:
   找东西:var,function,参数
   根据var 找到变量a后,不会读取变量a的值而是把变量a赋值为 undefined可以理解为偷懒机制,万一变量a是一大串json或者数组
   ***所有的变量,在正式运行代码之前,都提前赋了一个值undefined ***
   ***所有的函数,在正式运行代码之前,都是整个函数块 ***
   ***函数的声明,不改变任何值***
   ***遇到重名的:只留下一个,后面的覆盖前面的,遇到变量和函数重名了,留下函数(与变量在前在后没有关系)***
   a=undefined
   fn1=function fn1(){alert(2);}
2:逐行读取代码:(去域中找)
   表达式:= + - * / % ++ -- ! 参数(参数本质就是一个局部变量) 等
   表达式可以修改域解析的值
   函数调用:
          1:JS的域解析:
             找东西:var,function,参数
             ....
             ....
          2:逐行读取代码:
          3:函数自身找不到,才会去script域中找
*/
//===============================script域
console.log(a);      //从域解析中找   此时的a是function a(){alert(4);}
var a=1;            //表达式改变了域解析的值
console.log(a);     //此时的a是1
function a(){alert(2);} //函数的申明,不改变任何值
console.log(a);     //此时的a是1
var a=3;
function a(){alert(4);}
console.log(a);     //此时的a是3,不能在写a()这样的代码了

//==============================函数域
var b=12;
function fb(){
    console.log(b);  //此时的b是 undefined
    var b=10;
    console.log(b);  //此时的b是 10
}
fb();                 //函数调用:开始新的域解析
console.log(b);      //此时的b是 12  全局的

//==============================函数域 和上面的区别就是函数中的c没有var
var c=22;
function fc(){
    console.log(c,'fc中');  //此时的c是22
    c=20;
    console.log(c,'fc中');  //此时的c是20
}
console.log(c);      //此时的c是22
fc();                //执行函数,改变了全局的c
console.log(c);      //此时的c是20

//==============================函数域 和上面的区别就是函数多了参数d
var d=32;
function fd(d){             //参数本质就是局部变量  function fd(var d)
    console.log(d,'fd中');  //此时的d是undefined
    d=20; //由内而外找,自己有就用自己的
    console.log(d,'fd中');  //此时的d是20
}
console.log(d);      //此时的d是32
fd();
console.log(d);      //此时的d是32

//==============================函数域 和上面的区别就是传参了
var g=32;
function fg(g){
    console.log(g,'fg中');  //此时的g是32
    g=20;
    console.log(g,'fg中');  //此时的g是20
}
console.log(g);      //此时的g是32
fg(g); //把全局的g当参数传进去了
console.log(g);      //此时的g是32

//==============================函数域 和上面的区别就是传参了
var e=32;
function fe(e){
    console.log(e,'fe中');  //此时的e是5
    e=20; //因为有参数,所以是局部变量,与var e=20一样
    console.log(e,'fe中');  //此时的e是20
}
console.log(e);      //此时的e是32
fe(5);
console.log(e);      //此时的e是32

//==============================遇到变量和函数重名了,留下函数(与变量在前在后没有关系)
var s=10;
function foo(){
  console.log(s);  //function s(){console.log("ok")}
  function s(){console.log("ok")}
  console.log(s);  //function s(){console.log("ok")}
  var s=5;        //遇到表达式改变值
  console.log(s);  //5
}
foo();

//==============================想要获取函数内的值 方式1:
var str='';
function fn(){
  var a='1个亿';
  str=a;
}
fn();
console.log(str);

//==============================想要获取函数内的值 方式2:
function fn2(){
  var a='2个亿';
  fn3(a);
}
fn2();
function fn3(a){
  console.log(a);
}
//==============================*****不能对下面的函数进行域解析,代码写规范
console.log(a99);
console.log(fn99);
if(true){
  var a99=1;
  function fn99(){
    alert(23323);
  }
}
//==============================*****随便点击那个按钮,3个按钮的颜色变黄
window.onload=function(){
  var aBtn=document.getElementsByTagName("input");
  for(var i=0;i<aBtn.length;i++){
    aBtn[i].onclick=function(){
      //aBtn[i].style.background="yellow";  //报错了,此时的i是3,for循环执行速度很快的,是for执行完了以后你才能点的
      for(var i=0;i<aBtn.length;i++){
        aBtn[i].style.background="yellow";
      }

    }
  }
};

//==============================
function bar(age) {
    console.log(age);
    var age = 99;
    var sex= 'male';
    console.log(age);
    function age() {
        alert(123)
    };
    console.log(age);
    return 100;
}
alert(result=bar(5)); //function age() {alert(123)} ,99,99
/*
1、域解析(涉及参数,局部变量声明,函数声明表达式):
    1-1 、分析参数,有一个参数,形成一个 AO.age=undefine;
    1-2 、接收参数 AO.age=5;
    1-3 、分析变量声明,有一个 var age, 发现 AO 上面有一个 AO.age ,则不做任何处理
    1-4 、分析变量声明,有一个 var sex,形成一个 AO.sex=undefine;
    1-5 、分析函数声明,有一个 function age(){} 声明, 则把原有的 age 覆盖成 AO.age=function(){};
2、逐行解读:
    2-1 、执行第一个 console.log(age) 时,当前的 AO.age 是一个函数,所以输出的一个函数
    2-2 、这句 var age=99; 表达式改变了AO.age的属性值, AO.age=99 ,所以在第二个输出的age是 99;
    2-3 、同理第三个输出的是 99, 因为中间没有改变 age 值的语句了。
*/

</script>
<input type="button" value="按钮1">
<input type="button" value="按钮2">
<input type="button" value="按钮3">
</body>
</html>
View Code

 

posted @ 2019-12-25 21:44  silencio。  阅读(303)  评论(0编辑  收藏  举报