前言
- 现在Android开发需求越来越少了,快过年也没什么事,公司叫我学学前端,也罢了,技多不压身.
变量的声明:
- js中使用var关键字声明变量,同时变量名是大小写敏感的,即A与a是两个不同的变量.如果只是声明了变量而没有赋值
var a ;
那么a的值就是undefined
,这是js中的一个关键字,表示未定义.js是一门动态类型语言,这意味着你可以var a = 1; a = "hello"
,赋值不同类型.
变量提升
- 在js中,js引擎会将所有代码的声明全部先解析,然后再一行行执行语句,这就意味着所有变量会被提到头部执行,例如
console.log(a);var a = 1
等价于var a ; console.log(a);a=1;
,所以良好的编程习惯还是得先声明变量再使用.
区块
-
{}
以一对花括号表示,对于var
来说,区块不构成单独作用域,也就是说
{
var a = 1;
}
console.log(a);
在控制台上会输出a=1;这里说一下js的局部变量只在函数内部才能声明,区块中无效.可以理解为var任然是全局变量.
标签
- 作用与kotlin中的标签一样,主要用来跳出循环等操作,声明格式:
label:语句
,下面是一个例子
top:
for(var i=0;i<10;i++){
for(var j=0;j<10;j++){
if(i===j===1){
return top;
}
}
}
数据类型转换
- js是一种动态类型语言,变量类型可以随意赋值,但是使用运算符时,如果变量类型不符合预期类型则会自动转型,举个栗子:
"4"-"1"
,这里使用了-
运算符,要求元素类型为数值,所以js会自动将字符串转为数值.运行结果为3
,使用typeof
输出结果为number
类型,说明结果被自动转型了. - js也提供了几个可以强制转型的方法
Number()
,Boolean()
,String()
,下面分开细说这几个方法
Number()
- 分为参数为原始类型与对象类型,原始类型包括数值,字符串,布尔,下面贴上一段测试代码
//纯数字字符串
console.log(Number('321'));
//字符串
console.log(Number('321abc'));
//true
console.log(Number(true));
//false
console.log(Number(false));
//对象 null
console.log(Number(null));
//对象 undefined
console.log(Number(undefined));
接下来我们来看看输出的结果情况:
可以看到当参数是无法解析的字符串时例如包含
abc
输出结果为
NaN(not a number)
,
接下来我们来看看参数传对象试试,
console.log(Number({name:"xiaoming"}));
,这个时候输出了
NaN
.
这里说一下
Number()
的转换过程
- 首先调用自身的
valueof
方法,如果返回原始类型,则调用Number()
方法,否则进行下一步. - 如果
valueof
方法返回的是对象,则调用toString()
方法,如果toString()
返回原始类型则调用Number()
方法 - 如果
toString
还是返回对象,则返回NaN
String()
- 该方法可以将任意类型转为字符串,
console.log(String({name:"xiaoming"}));//[object object]
console.log(String(true));//'true'
console.log(String(false));'false'
console.log(String(null));'null'
console.log(String(undefined));'undefined'
Boolean()
- 除以下五个值为false,其余皆转换为true
undefined,null,+0/-0,NaN,''(空字符串)
值得一提的是,所有对象的转化结果都是true,甚至new Boolean(false)
也是true
错误机制
- JS通过Error对象来构造一个错误实例,它包括message属性与name属性,当然还有stack堆栈属性.
下面介绍一下JS中原生的Error对象:
1.SyntaxError:语法错误
2.ReferenceError:引用错误
3.RangeError:区间错误,通常是操作数组不当时发生
4.TypeError :类型错误,通常使用参数或者变量不是预期类型时发生
5.URIError :Uri相关错误 - Try Catch
和其他语言一样,JS提供了try catch
语句帮组我们捕获错误,当然也同样可以使用finally
,你也可以使用throw
关键字抛出一个错误.
函数
- 我们知道JS是一门函数式的编程语言,所以函数在JS中是一等公民一样的存在,函数可以作为参数,返回值,表达式等使用.
- 在JS中声明一个函数如下:
function name(params) {
}
使用fuction
关键字进行声明.因为JS的函数与大部分语言都很类似,接下来讲讲JS中使用函数值得注意的一些地方.
1.如果声明了多个同名函数,那么后面声明的函数会覆盖前面的函数
function name(params) {
console.log(1);//1
}
function name(params) {
console.log(2);//2
}
name();
上面这段代码输出的结果为2,相信大家也注意到了,我声明的函数name
有一个参数,但是我在调用它的时候并没给参数,一样不报错可以执行.这也是JS的函数一个不同点,这里用到一个知识点agrument
等会再讲.在这里如果要查看定义函数的参数个数可以使用length
属性,它返回函数在定义时的参数个数,比如上面上的代码的调用name.length
则返回1.此时无论你在调用时输入多少个参数,它始终返回的是函数定义时的参数
函数的作用域
这里只讲ES5的作用域,因为ES6的我也没有学- -,JS中作用域分为:全局作用域和局部作用域
- 全局作用域:变量在整个程序中都存在,任何位置都可以访问
- 局部作用域:变量只能存在于函数内部
我们用一段代码来看看这个定义
var a = 1;
function name(params) {
var a = 2;
console.log("局部作用域:" + a);
}
name();
console.log("全局作用域:" + a);
可以看到局部作用域只在函数内部有作用,同时函数内的a覆盖了全局变量.同时注意函数内部的 变量提升,接下来我们改造下代码看看
var a = 1;
function name(params) {
console.log("局部作用域:" + a); //undefined
var a = 2;
console.log("局部作用域:" + a);//2
}
name();
console.log("全局作用域:" + a);//1
这个时候第一句console
输出了undefined
,是不是感觉应该输出全局变量1
,这里就是JS的变量提升在搞怪了,上面的代码在JS引擎解析时候会变成这样:
function name(params) {
var a ;
console.log("局部作用域:" + a); //undefined
a = 2;
console.log("局部作用域:" + a);//2
}
将函数内部的var a =2
变量a
提到了头部,因为a
在头部没有赋值,所以是undefined
了
函数本身的作用域
- 由于在JS中函数是一等公民一样的存在,你就可以把它和变量看做是一样的,它的作用域和变量也是一样的,就是在其声明时的作用域.举个例子
var a =1;
//全局作用域
function name(params) {
console.log(a);
}
//函数内部调用
function test(params) {
var a = 2;
name();
}
test();//1
我们在看一个作用域在函数内部的
var a =1;
//函数内部调用
function test(params) {
var a = 2;
return function name(params) {
console.log(a);
}
}
var f= test();
f();//2
上面这种结构又被称为闭包
参数
在JS中,参数是可以省略的,我们来看看下面的代码
function name(a, b) {
console.log(a + b);
}
name();//NaN 因为没传参数,所以a,b都是undefined,他们进行相加返回NaN
name(1);//NaN
name(1, 2);//3
- 在JS中根据传递的参数的类型
- 传原始数值:传值传递,函数内部修改不影响原值(类似形参)
var a = 1;
function name(params) {
params = 3;
console.log(params);//3
}
name(a);
console.log(a);//1
2.传对象:传址传递,会修改对象的内存地址,修改参数会影响到原值(类似实参)
var people = {
name:"xiaoming"
}
function test(params) {
params.name="xiaomi"
}
test(people);
console.log(people.name);//xiaomi
此时people.name
已经变更成了xiaomi
agrument对象
这个对象主要用来在函数内部获取参数,因为js允许参数数目不定,所以需要argument来获取,它包括了函数运行时的所有参数,这里所几个常用的属性
- callee:返回它对应的函数
- length:返回函数调用时的参数个数
闭包
要理解闭包得先了解作用域,我们知道在函数的内部是可以访问全局变量的,但是外部是无法访问函数内部的变量的,但是有时候我们又需要在外部访问函数内部的变量怎么办?
我们先看下这段代码
function f1(params) {
var a = 666;
return function f2(params) {
return a;
}
}
//返回函数f2
var f2 = f1();
//返回值a
var a = f2();
console.log(a);//666
我们要想拿到函数内部的变量a
,又重新定义了一个函数f2
,对函数f2
而言,f1
内部的变量是可以访问的,但是f2
却是对f1
不可访问的,这就是JS的链式作用域,因为函数在JS中是可以作为返回值的,我们使f2
携带f1
中的变量a
并作为f1
的返回值返回不就可以最终访问到a
了吗?
总结一下闭包得两个特点:
1.读取函数内部变量
2.使这些变量始终在内存中,通过栗子说明:
function f1(params) {
return function f2() {
return params++;
}
}
var a = f1(5);
console.log(a());//5
console.log(a());//6
console.log(a());//7
可以看到参数的值都在递增,说明上一次调用时候返回值是保存在了内存当中
3.闭包还有个用处就是用来封装私有属性和函数
函数立即执行
格式:函数+(),在函数后面立马跟上一个圆括号,表示该函数立即执行
通常这么定义(function(){}())
在外面再加一个大括号,防止JS引擎解析时产生歧义.