1.1 1.6 判断一个对象的类型
如何判断一个对象的类型? 有同学讲用 typeof 经过前面复习时对typeof的演示,我们已经知道,它只能区分基本数据类型。对于对象类型,就无法区分,返回都是object! 大家回忆 Java中如何判断类型? 1)instance of 2)通过类的字节码Class对象 事实上在JavaScript中,也是类似的方式。
1.6.1 方式1:instanceof instanceof用来判断一个对象是否属于某一个类
案例代码: [JavaScript] 纯文本查看 复制代码 // 演示: 判断对象的类型[/align]function Person() {
}
var p = new Person();
console.log(p instanceof Person); // true
console.log([] instanceof Array); // true
console.log(new Date() instanceof Date); // true
console.log(p instanceof Array); // false
console.log(p instanceof Object); // true
结论:
instanceof 可以基本准确的判断对象的所属类型,但是当 instanceof后面跟的是类型是父类类型的时候,判断是不准确的。这一点与Java类似 要更准确的判断对象类型,怎么办? Java中是通过类的字节码Class对象来进行准确判断的。但是JS中是没有类的概念的,更不要说Class对象,但是JS中类似的概念是构造函数constructor
16.2 方式2:constructor
constructor 用来检测直接的构造者
通过constructor来判断对象类型: [JavaScript] 纯文本查看 复制代码 <script>
// 演示: 判断对象的类型
function Person() {
}
var p = new Person();
console.log(p.constructor == Person); // true
console.log([].constructor == Array); // true
console.log(p.constructor == Array); // false
console.log(p.constructor == Object); // false
</script>
1.1 1.7 this关键字 在Java 中,this 关键字表示的是当前对象。比如在方法中使用,就是调用方法的对象。
在JavaScript中,this也是类似的。只不过JS中方法无需定义在类中,所以很多人对this的含义就懵逼了。
看案例:
[JavaScript] 纯文本查看 复制代码 <script>
// 演示: this关键字的含义
// this指的是当前的方法属于谁,只看调用,谁调用就指向谁,所以我们只需要搞清楚调用方法的对象即可
// 1. 全局函数中的this
function show() {
console.log('全局函数中的this:' + this);
}
// 全局函数 默认是注册到window对象上中
// 而调用window中的任何成员,默认是可以省略window.的
// 这里的show()完整的写法其实是:window.show(); 因此肯定当前的方法属于window这个对象
// show(); // Object window
window.show();
// 2. 对象中的this
var p = {
name: 'Jack',
age: 20,
gender: 'male',
sayHello: function() {
console.log('Hello, I am' + this.name + ',' + this.age + 'years old');
}
};
// 对象中的this,指向的是调用该方法的对象,现在该方法属于p这个对象,因此this指向了p
p.sayHello(); // 'Hello, I amJack,20years old'
// 3. 事件函数中的this
// 当前的事件注册到了document对象身上
// 事件触发时,调用该事件处理函数的对象就是事件源对象,也就是document对象,此时事件函数里面的this指向了事件源对象
document.onclick = function() {
console.log('事件函数中的this:' + this); //object HTMLDocument
};
// 4. 定时器里面的this
// setTimeout方法本身就是window对象的一个方法,那么时间到了以后,
// 定时器里面的函数有谁来调用? window, 所以定时器里面的this就是window
setTimeout(function() {
console.log('定时器里面的this:' + this); // object Window
}, 1000);
</script>
结论:1) 其实记住一点就够了:this指的是当前的方法属于谁,只看调用,谁调用就指向谁 2) 对象外的全局函数,this是window 3) 对象中的方法,被对象调用时,this是对象自己 4) 事件处理函数,this是事件绑定的对象 5) 定时任务函数,this是window
1.1 1.8 对象的继承面向对象的三大特征之一就是:继承。在JavaScript中是否可以完成继承呢?
1.8.1 组合式继承
什么是组合式继承?
组合式继承就是把 一个对象的属性 全部加到另一个对象中。也被称为属性拷贝。
组合式继承主要用在 对象的直接量 继承另一个 对象的直接量时。
案例代码:
[JavaScript] 纯文本查看 复制代码 <script>
// 演示:组合式继承 属性的copy
// 组合式继承适用于 对象的直接量(JSON)
var o1 = {
name : 'Jack',
age : 20,
gender : '男'
};
var o2 = {
math : 66,
english : 77,
chinese : 88
};
// 需求:o1要继承o2
// for (var x in o2) {
// o1[x] = o2[x];
// }
console.log(o1);
// 不好 不够智能
// 给o1这个对象添加一个方法, 而这个方法专门用来继承
o1.extend = function(obj) {
for (var x in obj) {
this[x] = obj[x];
}
};
o1.extend(o2);
console.log(o1);
</script>
结果:
使用的场景:比如我用对象封装了一个框架,现在要对该框架的方法进行扩展,新增更多方法。此时就可以把新增的方法定义到新的对象中,使用组合式继承来完成 方法的扩展。 通过一个匿名对象,给o1扩展更多方法: [JavaScript] 纯文本查看 复制代码 // 再一次扩展方法[/align]o1.extend({
sayHello : function() {
console.log('hello');
},
sayBye : function() {
console.log('bye bye');
}
});
o1.sayHello();
o1.sayBye();
结果:
1.8.2 构造函数继承
如果对象不是 直接量对象,而是通过构造函数new 出来的,那么如何继承另一个构造函数中的属性呢?
案例代码: [AppleScript] 纯文本查看 复制代码 <script>[/align]// 构造函数创建对象的继承
function Person(name,age,gender) {
// 属性
this.name = name;
this.age = age;
this.gender = gender;
}
// 通过原型添加共享方法
Person.prototype.sayHello = function() {
console.log('Hello, I am :' + this.name + ',' + this.age + 'years old');
};
// 工人 工人是人 所以可以继承
function Worker(name,age,gender,job) {
// 属性
Person(name,age,gender);
// 自己的属性
this.job = job;
}
var w = new Worker('Jack','20','male','Java Engineer');
console.log(w.name); // undefined
console.log(w.job); // 'Java Engineer'
</script>
结果:
我们发现直接在子类的构造函数中直接调用父类的构造函数不能实现属性的继承,因为直接调用父类的构造函数,此时父类的构造函数属于全局函数,所以里面的this指向了window对象,不是Worker的实例,这个时候我们要去改变父类构造函数里面this的指向,让父类构造函数里面的this指向Worker的实例 函数也是对象,任何函数都可以调用两个方法:call和apply ,来自于Function的原型call和apply都会导致调用的函数被执行,但是可以在执行时指定this: 名称 | | | | fn.call(fn函数里面this的值,参数1,参数2..); | 第一个参数是被调用函数里面this的值,真实的参数从第二个开始 | | fn.apply(fn函数里面this的值, [参数1,参数2..]); | |
[AppleScript] 纯文本查看 复制代码 <script>
// 构造函数创建对象的继承
function Person(name,age,gender) {
// 属性
this.name = name;
this.age = age;
this.gender = gender;
}
// 通过原型添加共享方法
Person.prototype.sayHello = function() {
console.log('Hello, I am :' + this.name + ',' + this.age + 'years old');
};
// 工人 工人是人 所以可以继承
function Worker(name,age,gender,job) {
// 属性
// 使用 call 或者 apply 来完成属性的继承
// Person.call(this,name,age,gender);
Person.apply(this,[name,age,gender]);
// 自己的属性
this.job = job;
}
// 创建Worker的实例
var w = new Worker('Jack',20,'male','Java Engineer');
console.log(w.name); // undefined
console.log(w.job); // 'Java Engineer'
// 尝试使用instanceof来检测Worker
console.log(w instanceof Worker); // true
console.log(w instanceof Person); // false
// 尝试调用继承过来的方法
w.sayHello(); // 报错 w.sayHello is not a function
</script>
结果:
我们发现,worker具备了父类中的所有属性,但是,Person类在原型中扩展的方法 sayHello 并没有被继承。访问时报错 并且,在执行instanceof的时候,无法判断worker属于Person类的实例 特点: 1) 可以继承父类构造函数中定义的属性 2) 父类原型中定义的方法无法访问,扩展性差 3) instanceof 判断失效,因为子类原型与父类原型没有关联
1.8.3 原型继承
为了解决上面的问题,我们可以采用原型继承的方式。
案例代码: [JavaScript] 纯文本查看 复制代码 <script>[/align]// 构造函数创建对象的继承
function Person(name,age,gender) {
// 属性
this.name = name;
this.age = age;
this.gender = gender;
}
// 通过原型添加共享方法
Person.prototype.sayHello = function() {
console.log('Hello, I am :' + this.name + ',' + this.age + 'years old');
};
// 工人 工人是人 所以可以继承
function Worker(name,age,gender,job) {
// 属性
// 使用 call 或者 apply 来完成属性的继承
// Person.call(this,name,age,gender);
Person.apply(this,[name,age,gender]);
// 自己的属性
this.job = job;
}
// 让子类的原型指向父类的实例。这样子类的原型就具备了父类的所有属性,子类的实例自然就具备了父类的属性
Worker.prototype = new Person();
// 修正子类原型的constructor
Worker.prototype.constructor = Worker;
// 创建Worker的实例
var w = new Worker('Jack',20,'male','Java Engineer');
console.log(w.name); // undefined
console.log(w.job); // 'Java Engineer'
// 尝试使用instanceof来检测Worker
console.log(w instanceof Worker); // true
console.log(w instanceof Person); // true
// 尝试调用继承过来的方法
w.sayHello(); // 'Hello, I am Jack,20 years old'
</script>
JS 的继承步骤:1) 在你的构造函数中,调用父类的构造,但是要用call或apply。 2) 让你的构造函数的prototype指向父类的对象。 3) 因为原型被覆盖,因此原型中的constructor也被覆盖,需要修正constructor指向子类的构造函数。 注意:原型继承,其实是让子类原型指向了父类对象。 这样父类对象就 覆盖了子类prototype中的所有属性。其中constructor属性也被覆盖成了父类的。因此,在使用原型继承时,必须手动把子类的原型中的constructor属性修改为子类的构造函数!
|