文章目录
1、instanceof
instanceof用于识别对象的类型。与 typeof 方法不同的是,instanceof 方法要求开发者明确地确认对象为某特定类型。
例1:
var s = new String("hello world");
console.log(s instanceof String); // 输出 "true"
例2:
// 判断 f 是否是 Foo 类的实例
function Foo(){}
var f = new Foo();
console.log(f instanceof Foo); //输出 "true"
2、typeof
typeof是一个运算符,用于判断变量类型,有2种使用方式:typeof(表达式)和typeof 变量名,第一种是对表达式做运算,第二种是对变量做运算。
console.log(typeof a); //'undefined'
console.log(typeof(true)); //'boolean'
console.log(typeof '123'); //'string'
console.log(typeof 123); //'number'
console.log(typeof NaN); //'number'
console.log(typeof null); //'object'
var obj = new String();
console.log(typeof(obj)); //'object'
var fn = function(){};
console.log(typeof(fn)); //'function'
console.log(typeof(class c{})); //'function'
3、call函数
call() 方法是预定义的 JavaScript 方法。
它可以用来调用所有者对象作为参数的方法。
通过 call(),您能够使用属于另一个对象的方法。
本例调用 person 的 fullName 方法,并用于 person1:
var person = {
fullName: function() {
return this.firstName + " " + this.lastName;
}
}
var person1 = {
firstName:"Bill",
lastName: "Gates",
}
var person2 = {
firstName:"Steve",
lastName: "Jobs",
}
person.fullName.call(person1); // 将返回 "Bill Gates"
4、6种继承方式
js继承的6种方式:
先写个父类:
function a(i){
this.i = i;
this.j = function(){
alert(this.i)
}
}
a.prototype.n = 10; // 给构造函数添加原型属性
4.1、原型链继承
function b(){
this.i = 1;
}
b.prototype = new a(); // a1继承自a
var b1 = new b();
console.log(b1.n) // 输出10
特点:1、实例可继承的属性有:实例的构造函数的属性,父类构造函数属性,父类原型的属性。(新实例不会继承父类实例的属性!)
缺点:1、新实例无法向父类构造函数传参。
2、继承单一。
3、所有新实例都会共享父类实例的属性。(原型上的属性是共享的,一个实例修改了原型属性,另一个实例的原型属性也会被修改!)
4.2、借用构造函数继承
function c(){
a.call(this,"cccc");
this.n = 12;
}
var c1 = new c();
console.log(c1.n) // 输出12
console.log(c1.i) // 输出“cccc”
重点:用.call()和.apply()将父类构造函数引入子类函数(在子类函数中做了父类函数的自执行(复制))
特点:1、只继承了父类构造函数的属性,没有继承父类原型的属性。
2、解决了原型链继承缺点1、2、3。
3、可以继承多个构造函数属性(call多个)。
4、在子实例中可向父实例传参。
缺点:1、只能继承父类构造函数的属性。
2、无法实现构造函数的复用。(每次用每次都要重新调用)
3、每个新实例都有父类构造函数的副本,臃肿。
4.3、组合继承(组合原型链继承和借用构造函数继承)(常用)
function d(i){
a.call(this,i);
}
d.prototype = new a();
var d1 = new d("dddd");
console.log(d1.n) // 输出10
console.log(d1.i) // 输出“dddd”
重点:结合了两种模式的优点,传参和复用
特点:1、可以继承父类原型上的属性,可以传参,可复用。
2、每个新实例引入的构造函数属性是私有的。
缺点:调用了两次父类构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函数。
4.4、原型式继承
// 先封装一个函数容器,用来输出对象和承载继承原型
function e(obj){
function f(){}
f.prototype = obj;
return new f();
}
var a1 = new a();
var e1 = e(a1);
console.log(e1.n) // 输出10
重点:用一个函数包装一个对象,然后返回这个函数的调用,这个函数就变成了个可以随意增添属性的实例或对象。object.create()就是这个原理。
特点:类似于复制一个对象,用函数来包装。
缺点:1、所有实例都会继承原型上的属性。
2、无法实现复用。(新实例属性都是后面添加的)
4.5、寄生式继承
在4.4原型式继承上,再套个壳子传递参数
function e(obj){
function f(){}
f.prototype = obj;
return new f();
}
var a1 = new a();
function g(obj){
var h = e(obj);
h.i = "gggg"
return h
}
var g1 = g(a1);
console.log(g1.i) // 输出"gggg"
重点:就是给原型式继承外面套了个壳子。
优点:没有创建自定义类型,因为只是套了个壳子返回对象(这个),这个函数顺理成章就成了创建的新对象。
缺点:没用到原型,无法复用。
4.6、寄生组合式继承(常用)
寄生:在函数内返回对象然后调用
组合:1、函数的原型等于另一个实例。2、在函数中用apply或者call引入另一个构造函数,可传参
function e(obj){
function f(){}
f.prototype = obj;
return new f();
}
var e1 = e(a.prototype);
function m(){
a.call(this);
}
m.prototype = e1; //继承了e1实例
e1.constructor = m; //修复实例
var m1 = new m();
console.log(m1.n); // 输出10
重点:修复了组合继承的问题
- 继承这些知识点与其说是对象的继承,更像是函数的功能用法,如何用函数做到复用,组合,这些和使用继承的思考是一样的。上述几个继承的方法都可以手动修复他们的缺点,但就是多了这个手动修复就变成了另一种继承模式。
- 这些继承模式的学习重点是学它们的思想,不然你会在coding书本上的例子的时候,会觉得明明可以直接继承为什么还要搞这么麻烦。就像原型式继承它用函数复制了内部对象的一个副本,这样不仅可以继承内部对象的属性,还能把函数(对象,来源内部对象的返回)随意调用,给它们添加属性,改个参数就可以改变原型对象,而这些新增的属性也不会相互影响。
5、constructor
constructor 属性返回对创建此对象的数组函数的引用。
例1:
<script type="text/javascript">
var test=new Array();
if (test.constructor==Array)
{
document.write("aaa");
}
if (test.constructor==Boolean)
{
document.write("bbb");
}
</script>
// 输出:aaa
例2:
<script type="text/javascript">
function e(name,job,born)
{
this.name=name;
this.job=job;
this.born=born;
}
var e1=new e("Bill Gates","Engineer",1985);
document.write(e1.constructor);
</script>
// 输出:
function e(name, job, born)
{this.name = name; this.job = job; this.born = born;}