1. 程序异常
① try-catch语法 测试异常
try-catch语法代码如下:
try {
异常代码; try中可以承重异常代码,
console.log(“try”) 出现异常代码后,正确代码不会执行
} catch (e) {
console.log(“e:”+e); try中出现异常在e中展现出来
console.log(“catch”); 只有try中出现异常才执行这段代码
} finally {
console.log( 1 ); 无论try中有无异常,都会执行finally代码
}
② throw语法 抛出异常
throw(name: ”Tom”, age: 18) 抛出异常,抛出一个对象
2. 函数的预解析
代码如下:
console.log(fn1);
console.log(fn2);
var fn1= function () {};
function fn2(){}
表达式式函数: var fn1= function () {}; 用” ; ”结尾
特点:没有函数名,预解析过程var fn1声明提升,”=”号的赋值没有执行,console.log(fn1)结果为undefined;
声明式函数: function fn2(){} 不用” ; ”结尾
特点:有函数名(fn2),预解析过程已经将函数体赋值给了fn2,console.log(fn2)结果为function fn2(){}
3. 作用域经典问题
var arr = [ { name: 'Tom' },
{ name: 'Jack' },
{ name: 'Jim' },
{ name: 'Lily' } ];
for ( var i = 0; i < arr.length; i++) {
arr[ i ].sayHello = function () {
console.log( 'name = ' + arr[ i ].name );
};
}
for ( var j = 0; j < arr.length; j++ ) {
arr[ j ].sayHello();
}
代码执行会报错,因为当执行arr[j].sayHello()时,函数中arr[i].name才执行,此时i的值已经为4了,如果把代码改为:
for ( var i = 0; i < arr.length; i++ ) {
arr[ i ].sayHello();
}
则可以顺利执行,因为调用函数sayHello()时,对i进行了重新赋值,进入函数作用域内后arr[i]也就变为动态变化的值了
该问题的由来,代码如下:
<ul>
<li>①</li>
<li>② </li>
<li>③</li>
<li>④</li>
<li>⑤ </li>
</ul>
var list = document.getElementsByTagName( 'li' );
var i;
for ( i = 0; i < list.length; i++ ){
list[ i ].onclick = function () {
alert( list[ i ].innerHTML ); 点击进入函数作用域时i的值已经为5了
alert( this.innerHTML ); 将list[i]改为this,可以正常执行
};
}
4.函数相关参数
① arguments
arguments: 函数中默认的类似数组类型对象,里面存储了所有传入的参数
② 函数名.length
函数中所有参数的个数,若无参数则为0;
综合使用案例,封装一个extend扩展函数,代码如下:
function extend() { 这种写法,只支持最多传入两个参数
var args = arguments;
if (args.length == 1) {
for (var k in args[0]) {
this[k] = args[0][k];
}
} else {
for (var k in args[1]) {
args[0][k] = args[1][k];
}
}
}
var o1 = {name: "Tom"};
var o2 = {age: 18, gender: "male"};
var o3 = {goHome: "bus"};
extend(o1, o2); 将o2混入o1之中
o3.extend = extend;
o3.extend(o1); 将o1添加到o3之中
console.log(o3);
③ callee与caller
(1)callee: 当前函数的引用
语法:arguments.callee
function foo() {
console.log(arguments.callee === foo); 返回值为true
}
foo();
callee一般在函数内部,实现函数递归时,使用callee表示函数引用
(2)caller: 表示在fn1中调用fn2 兼容性不佳
语法:函数名.caller
function fn1() {
console.log(fn1.caller); 打印调用者fn2,打印结果如下:
} function fn2() { fn1() };
function fn2() {
fn1();
}
fn2();
5.eval(“ ”):动态执行函数
① eval(“var num=12;” )函数中的声明会在整个作用域起作用
② eval()与Function()的比较
eval:直接调用就执行eval(“alert(“执行”);”),代码执行
Function:需要调用才执行,var fn = new Function(“alert(“执行”)”),这个过程是生成了一个函数,调用函数fn();代码执行,函数内声明只会在函数内起作用
使用Function的立即执行函数(自调用函数):
( new Function(“alert(“执行”)” )) ();对函数使用 (函数)();括号结构相当于执行了函数
任意函数的立即执行:
(function fn() {
alert("立即执行");
})();
③ 将json字符串转换成对象
不严格的json格式(属性没有写双引号””)但js能够识别如下:
var data='[{name:"Tom",age:18,gender:"male"},{name:"Jim",age:20,gender:"male"]';
(1)使用eval(); 传入json使用( )括起来
var obj = eval(“(”+data+”)”); 转换后obj为json对象
*使用eval()为何需要将data用圆括号括起来( ),变成表达式:
a ) 标识符格式:任意名 + :,例如:label:
标识符的使用:
label:
while (true) {
console.log("第一层循环");
while (true) {
console.log("第二层循环");
while (true) {
console.log("第三层循环");
break label;
console.log("第三层循环");
}
console.log("第二层循环");
}
console.log("第一层循环");
}
执行结果如上图所示,使用标识符后,从第三层循环直接跳出所有循环
b ) eval()中添加括号的解释:
var s1 = '{ }';
var o1 = eval(s1);
console.log(o1) 没有任何结果undefined
var s2 = '{name: "Tom"}';
var o2 = eval(s2);
console.log(o2) 输出”Tom”
var s3 = '{name: "Tom" , age: 18}';
var o3 = eval(s3);
console.log(o1) 直接报错
出现这种现象的原因是,eval( )将{ }当做代码块来执行,o2中的name:被当做标识符所以输出”Tom”,而o3中的name:, age:被判定为用” ,”号分割的标识符,是错误代码,所以直接报错,若将o3改为o3=eval(‘{name: “Tom” ; age: 18}’),使用” ; ”号分割,则输出18,原因是系统默认age:标识符替换了name:标识符,所以最后解决这个问题的办法是用( )将代码括起来,变成表达式,再给eval( )解析
(2)使用Function
var obj = (new Function(‘return’+data))();
(3)使用标准处理:JSON.parse(); 该方法必须使用严格的json格式,即属性必须写双引号””
var data='[{"name":"Tom","age":18,"gender":"male"},{"name":"Jim","age":20,"gender":"male"]'
var obj = JSON.parse(data);
6.函数的四种调用模式
①函数调用模式 func()
函数名+();
function func() {
console.log(this);
}
func();
函数是window this指向window
②方法调用模式 obj.func()
宿主对象obj调用函数,this指向引导方法的对象
function func() {
console.log(this);
}
func();
var obj = {name: "object"};
obj.func = func;
obj.func();
func():函数调用模式this指向window,obj.func():方法调用模式this指向obj
③构造器调用模式(constructor) 使用new关键字引导
new 是一个运算符,专门用来申请创建对象,创建出的对象传递给构造函数的this,利用构造函数对其初始化
(1)构造函数创建对象时的返回值(打印对象)
如果构造函数没有设置参数,在创建对象时( )可以不写,例如:
var obj = new Person;这样写即可
a ) 如果没有写返回值默认返回this
function Person() {
this.name = "Tom";
this.age = 18;
this.gender = "male";
}
var obj=new Person();
console.log(obj); Person {name: "Tom" , age: 18 , gender: "male"}
b ) 如果返回值是基本数据类型,number或string,则忽略返回类型,返回this
function Person() {
this.name = "Tom";
this.age = 18;
this.gender = "male";
return "string"||123;
}
var obj=new Person();
console.log(obj); Person {name: "Tom" , age: 18 , gender: "male"}
c ) 如果返回值是引用数据类型,[ ]或者{ },则忽略this,返回该数据类型
function Person() {
this.name = "Tom";
this.age = 18;
this.gender = "male";
return [ ] || { }; / return { } || [ ]
}
var obj=new Person();
console.log(obj); [ ] / { }
(2)综合分析题,代码如下:
function Foo(){
getName = function(){ alert(1); };
return this;
}
Foo.getName = function(){ alert(2); };
Foo.prototype.getName = function(){ alert(3); };
var getName = function(){ alert(4); };
function getName(){ alert(5); }
Foo.getName();
2 方法调用,只有alert(2)是该方法调用
getName();
4 函数声明提前,最后赋值为alert(4)
Foo().getName();
1 Foo()函数调用,返回window,进入函数体,getName赋值为alert(1),且被window调用,等于函数调用模式
getName();
1 函数调用模式,在上步已经赋值为alert(1)
new Foo.getName();
2 创建Foo.getName()构造函数的对象,等于该方法调用alert(1)
new Foo().getName();
3 new与()相结合,等于new Foo()构造函数创建的对象{}调用getName,由于构造函数没有定义getName属性,但是定义了Foo.prototype.getName,所以该对象{}继承了自己原型对象的getName属性alert(3)
new new Foo().getName();
3 同上步,new Foo()构造函数创建的对象{},所以该代码相当于new {}.getName(等于{}的方法调用模式创建对象,同理上一步,到Foo的原型对象找getName属性alert(3),这一步与上一步的区别是,上一步最后执行方法调用函数,这一步最后以上一步执行的方法调用函数为构造函数,创建对象
④上下文(环境)调用模式apply、call
(1)apply 参数以数组形式传入
a ) apply只传一个对象的函数调用
function fn() {
console.log(this);
}
var obj = {name: "Tom", age: 18};
fn();
fn.apply(null); apply中传入一个null或者不传参数 apply( ),等于函数调用模式
obj.func =fn;
obj.func();
fn.apply(obj); apply中传入一个对象,等于该对象的方法调用模式
上面代码的运行结果为:
fn()与fn.apply(null)等价,obj.func()与fn.apply(obj)等价
b ) apply传入参数的函数调用 数组参数
语法: function fn ( argu1 , argu2 ) { }; 函数
fn.apply ( obj , [argu1 , argu2] )
示例代码如下:
function fn(num1, num2) {
console.log(this);
return num1 + num2;
}
var obj = {name: "Tom", age: 18};
obj.func = fn;
console.log(obj.func( 123 , 456 ) );
console.log( fn.apply ( obj , [123, 456] ) );
c ) apply使用案例
一、 合并数组:
var arr1 = [1, 2, 3];
var arr2 = [4, 5];
arr1.push(arr2[0],arr2[1]);
[ ].push.apply(arr1,arr2); 这个方法与上面的方法等价
二、 合并标签对象:
<div>这是div标签1</div>
<div>这是div标签2</div>
<p>这是p标签1</p>
<p>这是p标签2</p>
将上述4个标签对象放入一个数组之中:
var byTag =document.getElementsByTagName,arr=[ ];
arr.push.apply(arr , byTag.apply(document, ["div"]));
arr.push.apply(arr , byTag.apply(document, ["p"]));
console.log(arr);
由于getElementsByTagName( )是document的方法调用函数,所以过byTag获取该方法后,该方法必须在document对象下调用,所以在apply中传入document对象
(2)call 参数以独立形势传入
call用法与apply除了参数传入形势不同以外,其他基本相同
语法: function fn ( argu1 , argu2) { };函数
fn.call ( obj , argu1 , argu2 )
借用构造方法创建对象:
function Person(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
function Student(name,age,gender,course) {
Person.call(this, name, age, gender);
this.course = course;
}
var student=new Student("Tom",20,"male","Math");
console.log(student);
7.补充知识
① bind调用 让函数绑定对象的一种方法,在调用函数时,就像是该对象在调用方法
语法:函数.bind(对象),返回一个函数
下列代码:
var byTag =document.getElementsByTagName,arr=[ ];
arr.push.apply(arr , byTag.apply(document, ["div"]));
arr.push.apply(arr , byTag.apply(document, ["p"]));
console.log(arr);
使用bind对代码进行修改:
var bindT = document.getElementsByTagName.bind(document), arr = [ ];
arr.push.apply(arr, bindT("div"));
arr.push.apply(arr, bindT("p"));
console.log(arr);
② Object.prototype基本成员介绍
(1)constructor
(2)hasOwnProperty判断属性是否为自己提供
console.log( obj.hasProperty(“name”));判断obj是否具有name属性
(3)propertyIsEnumerable 判断属性是否可以枚举
(4)isPrototypeOf判断是否为原型对象
console.log(fn.prototype.isPrototypeOf(obj)) 判断fn.prototype是obj的原型吗
(5)toSting( ) , toLocaleString( ) , valueOf( )
③包装类型 基本类型数据本不应该包含方法调用
基本类型数据,如数字,字符串在调用方法时,解释器先将基本类型数据转换成对应的对象类型,然后调用方法,执行方法后返回结果,同时销毁其转换的对象
④ get、set读写器
语法糖:为方便开发而给出的语法结构
var obj = (function () {
var num = 123;
return {
get num() {
console.log("执行getter读写器");
return num;
},
set num(value) {
console.log("执行setter读写器");
num = value;
return num;
}
}
})();
console.log(obj.num);
obj.num = 456;
console.log(obj.num);