1.1 今日目标
- 能够理解js中的内存分配和垃圾回收机制
- 能够使用for in循环遍历对象成员
- 能够使用delete运算符删除对象成员
- 能够理解什么是原型对象
- 能够理解原型链的访问规则
- 能够实现JS面向对象的继承
- 能够灵活使用call或apply
- 能够理解this在函数中和页面中的含义
1.2 属性
1.2.1 属性查找原则
<script>
function Student(name){
this.name=name;
}
Student.prototype.sex='男';
Object.prototype.age=22;
//测试
var stu=new Student('tom');
console.log(stu.name); //tom
console.log(stu.sex); //男
console.log(stu.age); //22
console.log(stu.add); //undefined
</script>
小结:
1、先在对象自身上查找是否有属性,如果有就返回结果
2、如果没有就顺着原型链往上查找,找到就返回结果
3、如果找不到就返回undefined
总之:沿着原型链向上查找。
1.2.2 属性设置原则
<script>
function Student(name){
this.name=name;
}
var stu=new Student('tom');
stu.name='berry'; //修改name属性
stu.sex='男' //添加sex属性
console.log(stu.name); //berry
</script>
小结:
对象有的属性就修改,没有的属性就添加
1.2.3 属性的特征
特征有:可修改、可枚举、可删除
<script>
var stu={
name:'tom'
};
//添加name属性
Object.defineProperty(stu,'sex',{
value:'男',
writable:false, //不能修改
enumerable:false, //不能枚举
configurable:false //不可以删除
});
//测试:不可修改
stu.sex='女'
console.log(stu); //{name: "tom", sex: "男"}
//测试:不可删除
delete stu.sex //删除sex属性
console.log(stu); //{name: "tom", sex: "男"}
//测试:不可枚举
for(var s in stu){
console.log(stu[s]); //tom
}
</script>
注意:删除对象的属性用delete。
1.3 函数高级
1.3.1 创建函数
方法一:直接声明
<script>
function fun(){
}
</script>
方法二:匿名函数
<script>
var fun=function(){
}
</script>
方法三:通过构造器定义
语法:
var 函数名=new Function(形参,形参,...,'函数体')
例题:
<script>
var fun=new Function('num1','num2','alert(num1+num2)');
fun(10,20);
</script>
多学一招:如果参数就一个,这个参数就是函数体
<script>
var fun=new Function('alert("锄禾日当午")');
fun();
</script>
应用例题:执行输入在文本域中的JS脚本
<script>
window.onload=function(){
document.getElementById('code').onblur=function(){
var fun=new Function(this.value); //将文本域的内容作为函数体
fun();
}
}
</script>
<textarea id="code" cols="30" rows="10"></textarea>
运行结果
1.3.2 函数的调用模式与this
1、函数调用模式:函数内的this表示window
<script>
function fun(){
console.log(this); //Window
}
fun(); //函数调用模式
//相当于
window.fun();
</script>
2、方法调用模式:方法内的this表示调用方法的对象
<script>
var obj={
fun:function(){
console.log(this); //obj对象
}
}
obj.fun(); //方法调用模式 {fun: ƒ}
obj['fun'](); //方法调用模式 {fun: ƒ}
</script>
3、构造函数模式:构造函数内的this表示实例化的对象
<script>
function Student(){
console.log(this); //this表示实例化的对象stu Student {}
}
var stu=new Student();
</script>
4、上下文模式
通过call()、apply()、bind()调用
1:调用函数
2::改变函数内部this的指向
小结:
1、如果this在html中表示当前标签对象
<img src='' onmouseover='this.src=""' /> this表示img标签
2、JS中,this在函数外部,总是表示window对象
<script>
console.log(this); //Window
</script>
3、函数调用模式,this表示window对象
4、方法调用模式,this表示调用方法的对象
5、构造函数调用模式,this表示实例化的对象
1.3.3 思考题
思考题一:
var age=10;
var obj={
age:20,
getAge:function(){
console.log(this.age);
}
}
obj.getAge(); //20
var f=obj.getAge;
f(); //10
思考题二:
var age=10;
var obj={
age:20,
getAge:function(){
console.log(this.age); //20
function show(){
console.log(this.age); //10
}
show();
}
}
obj.getAge();
思考题三:
var length=10;
function fn(){
console.log(this.length);
}
var arr=[fn,'111'];
fn(); //10 this表示window
arr[0](); //2 this表示arr
思考题四:
var length=10
function fn(){
console.log(this.length);
}
var obj={
length:5,
method:function(){
fn() //window.length 10
arguments[0](); //arguments 3
}
}
obj.method(fn,10,5);
小结:this指向与调用模式有关系,与在什么地调用没有关系。
1.3.4 函数本身也是对象
<script>
function fun(){ //fun本身也是对象
}
console.log(fun.__proto__.constructor); //Function
console.log(fun.__proto__.__proto__.constructor); //Object
</script>
原型三角关系
1.4 call()、apply()、bind()
作用:
1:调用函数
2::改变函数内部this的指向
1.4.1 call()
语法
call(对象,形参,形参,...)
1、通过call调用函数
<script>
function show(){
console.log(this); //Window
}
show.call(); 调用show()函数
</script>
2、改变函数内部this的指向
<script>
var name='李白';
function show(){
console.log(this.name); //通过call调用的函数中的this表示传递的对象
}
show(); //李白
show.call({name:'tom'}); // tom
</script>
好处:方法借用
例题一:
<script>
var stu={
name:'tom',
gift:'鲜花',
express:function(){
console.log('今天'+this.name+'送你'+this.gift+'作为礼物');
}
}
stu.express(); //今天tom送你鲜花作为礼物
stu.express.call({'name':'老王','gift':'车'}); //今天老王送你车作为礼物
</script>
例题二:伪数组调动数组的方法
说明:
伪数组:有数字做下标、有length属性,但是不能使用数组的方法
例题
<script>
//伪数组
var stu={
0:'tom',
1:'berry',
2:'ketty',
length:3
};
[].push.call(stu,'李白','杜甫');
console.log(stu); //{0: "tom", 1: "berry", 2: "ketty", 3: "李白", 4: "杜甫", length: 5}
</script>
1.4.2 apply()
apply的使用和call()是一样的,只是参数有变化
语法
apply(对象,形参数组)
例题:求最大数
<script>
var num=[10,20,35,48,12,17];
var max1=Math.max.call(num,10,20,35,48,12,17);
var max2=Math.max.apply(num,num);
console.log(max1,max2); //48 48
</script>
面试题:封装打印的方法
<script>
function print(){
[].unshift.call(arguments,'你好'); //借用unshift方法
console.log.apply(arguments,arguments); //借用log方法
}
print('tom','berry') //你好 tom berry
</script>
call()和apply区别
1、如果参数比较少,使用call()更加方便
2、如果参数已经在数组中,使用apply()方便
1.4.3 bind()
语法
fn.bind(参数) :创建并返回一个和fn一样的函数,函数内的this指向args参数
例题
<script>
var fn1=function(){
console.log(this);
}
fn1(); //Window
var fn2=fn1.bind(['tom']); //创建一个和fn1一样的函数
fn2(); //["tom"]
</script>
例题:
<script>
var name='李白';
var obj={
name:'tom',
sayHello:function(){
setInterval(function(){
console.log('你好:'+this.name); //你好:李白
}, 1000);
setInterval(function(){
console.log('你好:'+this.name); //你好:tom
}.bind(this), 1000);
}
}
obj.sayHello();
</script>
1.5 封装
js中封装体现在public、private上,js中不能直接使用这两个关键字。
public:在构造函数里通过this声明的成员都是公开的,在函数内部、外部都可以访问。
private:在构造函数里边声明局部变量,就是私有成员。
<script>
function Student(){
this.name='李白'; // 公有的
var phone='13654566' // 私有的
var test=function(){
console.log('这是一个私有的方法');
}
this.show=function(){ //公有的方法
test(); //在公有的方法中调用私有方法
console.log('这是一个公有的方法');
}
}
var stu=new Student();
console.log(stu.name); //李白
console.log(stu.phone); //undefined
stu.show();
</script>
1.6 继承
1.6.1 原型继承
语法:
构造函数.prototype.成员=成员值 --原型继承
构造函数.prototype=对象 --原型替换
例题:原型继承
<script>
function Student(){
this.name='tom';
}
//原型继承
Student.prototype.exam=function(){
console.log(this.name+'要考试');
}
//测试
var stu=new Student();
stu.exam(); //tom要考试
</script>
例题:原型替换
<script>
function Student(){
this.name='tom';
}
//原型替换
Student.prototype={age:20,add:'上海'};
var stu=new Student();
console.log(stu.name,stu.age,stu.add); //tom 20 上海
</script>
注意:原型替换以后,原型就没有constructor属性。
解决方法:手动添加constructor属性
<script>
function Student(){
this.name='tom';
}
Student.prototype={
age:20,
add:'上海',
constructor:Student //手动添加constructor属性
};
var stu=new Student();
console.log(stu.__proto__.constructor) // Student
</script>
1.6.2 混入式继承
特点:只继承自己没有的属性。将属性添加到对象上
<script>
function Student(){
this.name='学生';
}
//混入式继承
Student.prototype.extend=function(obj){
for(var k in obj){
if(this.hasOwnProperty(k)) //如果自己有这个属性就进入下一个循环
continue;
this[k]=obj[k];
}
}
//测试
var stu=new Student();
stu.extend({'name':'tom','age':20,'add':'上海'});
console.log(stu); //Student {name: "学生", age: 20, add: "上海"}
</script>
注意:hasOwnProperty:判断是否是自己的属性
1.6.3 混入+原型继承
特点:只继承自己没有的属性。将属性添加到对象的原型上
<script>
function Student(){
this.name='学生';
}
//混入式继承
Student.prototype.extend=function(obj){
for(var k in obj){
if(this.hasOwnProperty(k)) //如果自己有这个属性就进入下一个循环
continue;
this[k]=obj[k];
}
}
//混入+原型继承
Student.prototype.extend({'name':'tom','age':20,'add':'上海'})
var stu=new Student();
console.log(stu);
</script>
1.6.4 经典继承
特点:创建一个空对象,这个对象继承指定的对象
<script>
var stu={
name:'学生',
age:22
};
var obj=Object.create(stu); //创建一个空的对象,这个对象的原型是参数stu
console.log(obj); //{}
console.log(obj.__proto__); //stu对象
console.log(obj.name,obj.age); //学生 22
</script>
1.7.5 call和apply实现继承
<script>
function Person(name,sex){
this.name=name;
this.sex=sex;
}
function Student(name,sex,score){
//Person.call(this,name,sex);
Person.apply(this,arguments); //效果同上
this.score=score;
}
var stu=new Student('tom','男',88);
console.log(stu); //Student {name: "tom", sex: "男", score: 88}
</script>
1.7 多态
在JS中模拟多态
1、在函数内部使用arguments关键字实现多态
2、通过更改this指向可以实现多态(call和apply、bind)
1.8 静态成员
将成员添加到构造器上。
<script>
function Student(){
this.name='tom';
}
Student.add='中国'; //相当于添加了静态成员(添加在构造器上)
var stu=new Student();
console.log(stu.add); //undefined
console.log(Student.add); //中国
</script>
1.9 JSON对象
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。JSON格式和字面量或者数组格式是一样的。
在JS中,中括号表示数组,大括号表示对象
1.9.1 PHP生成JSON数据
<?php
//1、将索引数组转成JSON格式
$stu=array('tom','berry','ketty');
echo json_encode($stu),'<br>'; //转成JSON格式 ["tom","berry","ketty"]
//2、将关联数组转成JSON格式
$stu=array('name'=>'tom','sex'=>'男');
echo json_encode($stu),'<br>'; //{"name":"tom","sex":"\u7537"}
//3、将二维数组转成JSON格式
$stu=[
['name'=>'tom','age'=>20],
['name'=>'berry','age'=>22]
];
echo json_encode($stu),'<br>'; //[{"name":"tom","age":20},{"name":"berry","age":22}]
//4、将对象转成json格式
class Student{
public $name='tom';
private $add='上海';
}
$stu=new Student();
echo json_encode($stu); //{"name":"tom"}
1.9.2 JSON转成PHP格式
<?php
$str='["tom","berry","ketty"]';
print_r(json_decode($str)); //Array ( [0] => tom [1] => berry [2] => ketty )
echo '<br>';
$str='{"name":"tom","sex":"\u7537"}';
print_r(json_decode($str)); //stdClass Object ( [name] => tom [sex] => 男 )
19.3 JavaScript操作JSON数据
和操作字面量对象、数组是一样的。
<script>
var stu={
'name':'tom',
'score':{
'ch':80,
'math':90
}
}
console.log(stu.name); //tom
console.log(stu['score'].ch); //80
console.log(stu.score.math); //90
</script>
1.10 垃圾回收机制
机制一:引用计数算法,如果一个内存没有被引用,就被垃圾回收机制视为垃圾。
缺点:不能解决环形引用的问题
机制二:标记/清除算法:当程序执行完毕后,被变量做标记。
第一步标记变量,不清除的做个标记,清除的不用做标记
第二步遍历所有变量,清除没有标记的变量
1.11 异常Exception
代码一旦出错,后面的代码就终止执行,这样用户体验不好。
一旦有错误就抛出异常,处理完异常以后继续执行。
使用try、catch、finally关键字
<script>
try{
var obj=new 20();
}catch(ex){
console.log('错误:'+ex);
}finally{
console.log('finally块必须要执行')
}
console.log('锄禾日当午');
</script>
运行结果
1.12 调试技术
方法一:输出消息
document.write()
alert()
console.log()
console.dir()
方法二:异常处理
try-catch语句
方法三:添加断点
使用debugger关键字添加断点
使用浏览器调试工具添加断点
使用debugger添加断点
<script>
debugger; //添加断点
for(var i=1;i<=5;i++){
console.log(i);
}
</script>