变量提升
JavaScript 引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行地运行。这造成的结果,就是所有的变量的声明语句,都会被提升到代码的头部,这就叫做变量提升。
console.log(msg);
var msg = 'so easy';
// 变量提升,相当于下面的代码
var msg;
console.log(msg);
msg = 'so easy';
// 说明: 最后的结果是显示undefined, 表示变量msg已声明, 但还未赋值。
注意:变量可以不用var声明,直接写出来,变量提升只对 var 命令声明的变量有效,如果一个变量不是用 var 命令声明的,就不会发生变量提升。以下代码会报错,提示“ReferenceError: msg is not defined”,即变量 msg 未声明,这是因为 msg 不是用 var 命令声明的,JavaScript引擎不会将其提升。
console.log(error_msg);//会报错
error_msg = 'error';//可以不用var直接写出来声明一个对象
数据类型
js有6种数据类型:其中有五种简单的数据类型:undefined、Null、布尔、数值和字符串。一种复杂数据类型Object
数 值(Number): 整数和小数(比如 1 和 3.14) 字符串(String): 字符组成的文本(比如"Hello World") 布尔值(Boolean):true(真)和 false(假)两个特定值 undefined: 表示“未定义”或不存在,即此处目前没有任何值 Null: 表示空缺,即此处应该有一个值,但目前为空 对象(object)(引用) : 各种值组成的集合 a. 对象(object){name:”zhangsan”,age:”18”} b. 数组(array)[1,2,3] c. 函数(function)function test() {} |
多个数据可以在一个语句中打印,用逗号分隔
函数中的变量声明可以不写,毕竟都是var,如果是标识符,在函数中可以不写引号
NULL
null类型是只有一个值的数据类型,即特殊的值null。它表示空值,即该处的值现在为空,它表示一个空对象引用。 使用Null类型值时注意以下几点: a. 使用typeof操作符测试null返回object字符串。 b. undefined派生自null,所以等值比较返回值是true。所以,未初始化的变量和赋值为null的变量相等。 |
console.log(undefined == null); var box = null; // 赋值为null的变量 var a; // 未初始化的变量 console.log(a == box); // 两个的值相等 |
Boolean
字符串 | 转为true的值 | 转为false的值 |
Boolean | true | false |
String | 非空字符串 | 空字符串 |
Number | 非0数字(包括无穷大) | 0和NaN |
Object | 任何对象 | null |
undefined |
| undefined |
java中两边的操作数只能为booelan类型
* js中两边的操作数可以为其他类型
* 如果两边的操作数为其他类型,非boolean类型的数据出现的时候
* 结果肯定为其中一个操作数之一
依据短路原则,&&第一个为假,第二个就遍历不到,所以值为第一个值
||第一个为真,第二个就遍历不到,所以结果为第一个值
* && 第一个操作数转为boolean,如果为false,结果为第一个操作数
* 如果为true,结果为第二个操作数
* || 第一个操作数转为boolean,如果为false,结果为第二个操作数
* 如果为true,结果为第一个操作数
块
JavaScript 使用大括号,将多个相关的语句组合在一起,称为“区块”。JS中只有函数才有作用于作用域
与大多数编程语言不一样,JavaScript 的区块不构成单独的作用域。也就是说,区块中的变量与区块外的变量,属于同一个作用域。
{
var a = 10;
}
console.log("a-----" + a);
//10 可以获取到
上面的代码,在区块内部声明并赋值了变量a,然后在区块外部,变量a依然有效,这说明区块不构成单独的作用域,与不使用区块的情况没有任何区别。所以,单独使用的区块在 JavaScript 中意义不大,很少出现。区块往往用来构成其他更复杂的语法结构,比如 for、if、while等。
数组
特别灵活,不会发生数组越界,越界的会报undefined
遍历方式
普通for循环
var arr = [1, '2', 3.3, true, null];
for (var i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
for in 循环(类似foreach循环,但是一个是访问下标,一个是获得整个元素)
var arr = [1, '2', 3.3, true, null];
for (var i in arr) {
console.log(arr[i]);
}
forEach函数
//数组名.forEach(function(element, index){
//数组名[下标]是获取元素
}) // element(名称任意):元素,index(名称任意):下标
var arr = [1, '2', 3.3, true, null];
arr.forEach(function(element, idx) {
console.log(idx + "-->" + element);
});
数组的属性:
如果下标为正常的平时使用的时候(整数字符串也可以),没有的显示undefined,有就显示应该的
负数,小数,非数字字符串,这些不计算在长度里面,当成"属性"处理,相当于自定义属性
* for --> 不遍历属性
* foreach -->不遍历属性和索引中的undefined
* for in -->不遍历索引中的undefined
var arr = [1, '2', 3.3, true, null];
arr[-1.5] = '我是数组的属性';
arr['sxt'] = 'sxt';
console.log(arr[-1.5]);
console.log(arr['sxt']);
console.log(arr[-1]);
console.log(arr['1']); // 自动转换为1
//结果为:
我是数组的属性
sxt
undefined
2 (字符串)
数组提供的操作方法
Array对象为我们提供了一些方法,可以很方便地操作数组 push 添加元素到最后 unshift 添加元素到最前 pop 删除最后一项 shift 删除第一项 reverse 数组翻转 join 数组转成字符串 indexOf 数组元素索引 slice 截取(切片)数组,原数组不发生变化 一个参数为从当前位置开始截取到最后 二个参数为从第一个开始截取到第二个参数 splice 剪接数组,原数组变化,可以实现前后删除效果,被截取下来的从原来的数组中消失 一个参数表示当前位置开始截取到最后 二个参数从第一个开始截取第二个参数个 多个参数表示从 开始截取的地方从第三个及以后的填补被截取下来的原数组的内容 concat 数组合并 |
函数
有三种函数定义的方式:函数声明语句、函数定义表达式、Function构造函数
函数在传递参数的时候可以多传递也可以少传递,多传递选前面的,少传递其他的用undefined
声明函数
// 声明函数
function foo1() {
console.log('foo1');
}
// 调用函数
foo1();
函数定义表达式
以表达式方式定义的函数,函数的名称是可以不需要的 ,这种写法将一个匿名函数赋值给变量。这时,这个匿名函数又称函数表达式,因为赋值语句的等号右侧只能放表达式。
foo2();
// 声明函数
var foo2 = function () {
console.log('foo2');
}
// 调用函数
foo2();
函数也有提升问题,因为JS是把所有的东西都加载完了再去执行代码的,所以也有类型提升,第一个也可以执行
//函数表达式
var fn1= function func(i){
console.log("我是函数表达式1");
if(i==5){
return;
}
i++;
func(i);
};
console.log(fn1);
fn1(1);
// func();
这是递归 这种方式不能用func(8)这种方式来执行,只能用fn(1)来执行,打印fn1就是这个函数体
//直接调用
var fn2=function(){
console.log("我是函数表达式2");
}();
//fn2();报错
上述代码创建完了就调用了,所以fn2毫无意义,会报错
几种特殊的调用函数的方式:
//几种特殊的运用方式
!function fff(a){
console.log(1234);
}(1);
~function fff1(a){
console.log(456);
}(1);
+function fff2(a){
console.log(789);
}(1);
-function fff3(a){
console.log(234);
}(12);
(function fff4(a){
console.log('haha');
})(1);
关于函数内部的作用域问题,注意!!!!
<script>
// 若函数形参同名(一般不会这么干):在使用时以最后一个值为准
function add2(a, a) {
console.log('函数形参同名-----' + a);
}
add2(1, 2);
// 给参数默认值
function func(a=1, b=2) {
return a + b;
}
console.log('参数默认值-----' + func(4,5));
// 给形参o赋予了新的数组
var obj2 = [1, 2, 3];
function paramter2(o) {
// o = [2, 3, 4];
o[0] = 3;
}
paramter2(obj2);
console.log('引用传递-----' + obj2);
</script>
第一个函数测试的是参数同名,按照第二个来,第二个函数测试的是给参数值赋予默认值,第三个函数测试的是值传递,注意,java中只有传递值(个人觉得是地址值),如果是基本数据类型,就把常量池中的数据赋予给变量,在函数内部,作为局部变量,当变量重新赋值,或着说字符串(一个字符串用""书写的默认是一个字符串对象)或着是其他的引用类型 就会相当于把变量重新给一个新的对象,所以在函数外部的数据是不会变化的,但是作为数组,通过索引改变数据,会改变数据,所以外部的对象指向的那个对象其实已经被修改了
JS中的this关键字
this 指代对象 运行期为this绑定对象 决策树: 判定this指代的是哪一个对象 1.this所在的函数是通过new的使用调用么,如果是this指代new的对象 2.this 所在的函数是通过 XX.调用的么,如果是,this指代调用的对象 .前面的对象 3.如果都不是,默认this指代window(全局对象) 定义的全局变量默认相当于window对象的一个属性 定义的全局的方法,默认相当于window对象的功能
<script>
var a=1;
console.log(a);
console.log(window.a);
console.log(this.a);
var obj= {
a:12,
fn:function(x,y){
console.log(this.a,x,y)
}
};
obj.fn(3,4);
var f=obj.fn;
f();
window.f();
function Person(name,age){
this.name=name;
this.age=age;
}
var p1=new Person('zhangsan',18);
var p2=new Person('zhangsan2',19);
console.log(p1);
console.log(p2);
var obj2={
a:24
};
//apply()和call() 指定当前函数在调用的时候,内部的this知道哪一个对象
// 第一个参数就是指代的对象, call()第二个到多个参数为为调用函数的实参,apply方法的第二个参数为[],数组中存放实参
obj.fn();
obj.fn.call(obj2,2,3);
obj.fn.apply(obj2,[2,3]);
obj.fn();
//arguments 函数内置对象,在函数内部使用,动态的指定当前调用函数时传递的参数,操作数组的方式使用
//arguments 可以获取调取对象的参数值所有,包括没有真正传入到函数中的(函数的接收的变量个数不够)
function haha(){
console.log(arguments);
for(var i in arguments){
console.log(arguments[i]);
}
}
haha(1,2,3,4,5);
</script>
apply()和call()方法,指定当前函数在调用的时候,内部的this知道哪一个对象
第一个参数就是指代的对象, call()第二个到多个参数为为调用函数的实参,apply方法的第二个参数为[],数组中存放实参