JS重要知识点小结(2)


/****************** 面向对象 ******************/
/********** 一:对象创建、原型链 **********/
/* 讨论 构造函数(类方式)创建对象 ,深入理解这些内容,是很重要的
*/
function MyFunc() { }; //定义一个空函数
var anObj = new MyFunc(); //使用new操作符,借助MyFun函数,就创建了一个对象
// 等价于:
function MyFunc() { };
var anObj = {}; //创建一个对象
anObj.__proto__ = MyFunc.prototype;
MyFunc.call(anObj); //将anObj对象作为this指针调用MyFunc函数
/*
用 var anObject = new aFunction() 形式创建对象的过程实际上可以分为三步:
第一步:建立一个新对象(anObject);
第二步:将该对象内置的原型对象(__proto__)设置为构造函数prototype引用的那个原型对象;
第三步:将该对象作为this参数调用构造函数,完成成员设置等初始化工作。
对象建立之后,对象上的任何访问和操作都只与对象自身及其原型链上的那串对象有关,
与构造函数再扯不上关系了。
换句话说,构造函数只是在创建对象时起到介绍原型对象和初始化对象两个作用。
原型链:(参考:)
每个对象(此处对象应该仅指大括号括起来的object,不包括function、array。待验证?)
都会在其内部初始化一个属性,就是__proto__,当我们访问一个对象的属性时,
如果这个对象内部不存在这个属性,那么他就会去__proto__里找这个属性,
这个__proto__又会有自己的__proto__,于是就这样 一直找下去,也就是我们平时所说的原型链的概念。
*/
/* 理解了对象创建的原理,可试着分析下边两个示例的结果 */
var yx01 = new function() {return "圆心"};
alert(yx01); // [object Object]
var yx02 = new function() {return new String("圆心")};
alert(yx02); // “圆心”
/* 解释:
"圆心"是基本的字符串类型,new String("圆心")则创建了一个string对象。
只要new表达式之后的构造函数返回一个引用对象(数组,对象,函数等),都将覆盖new创建的对象,
如果返回一个原始类型(无 return 时其实为 return 原始类型 undefined),
那么就返回 new 创建的对象。
参考:
*/
/********** 二:数据类型的检测 **********/
/* 判断数据类型可能想到的方法:
constructor、typeof、instanceof、Object.prototype.toString.call()
*/
/***** 1、通过constructor属性 *****/
var myvar= new Array("a","b","c","d");
function A(){}
myvar.constructor = A;
var c = myvar.constructor;
alert(c); // function A(){}
//可见,通过constructor属性获取类型的方法很容易被修改,不应该用来判断类型。
/***** 2、通过typeof *****/
/*
typeof是一个操作符,而不是个函数。
typeof的实际应用是用来检测一个对象是否已经定义或者是否已经赋值。
如if(typeof a!="undefined"){},而不要去使用if(a)因为如果a不存在(未声明)则会出错。
typeof检测对象类型时一般只能返回如下几个结果:
number,boolean,string,function,object,undefined。
对于Array,Null,自定义对象 等使用typeof一律返回object,
这正是typeof的局限性。
*/
var num = new Number(1);
var arr = [1,2,3];
alert(typeof num); //object 而不是number
alert(typeof arr); //object 而不是Array
alert(typeof null); // object
/***** 3、通过 instanceof *****/
/* 用instanceof操作符来判断对象是否是某个类的实例。
如果obj instanceof Class返回true,那么Class的原型与obj原型链上的某个原型是同一个对象,
即obj要么由Class创建,要么由Class的子类创建。
*/
function t(){};
t.prototype = Array.prototype;
//t.prototype = [];
var x = new t();
alert(x instanceof t);//弹出true
alert(x instanceof Array);//弹出true
alert(x instanceof Object);//弹出true
/*
由此可知,通过 instanceof 判断数据类型也不可靠。
因为一个对象(此处x)的原型链可以很长,每个原型的类型可以不同。
另外在iframe内也会容易出错:
即有个页面定义了一个数组a,页面又嵌套了一个IFrame,在Iframe里面通过 top.a instanceof Array, 是返回false的。
这个说明 父页面和内嵌iframe里的对象是不同的,不能混合在一起使用。
改成top.a instanceof top.Array 就会返回true
*/
/***** 4、通过 Object.prototype.toString.call() *****/
/*
Object.prototype.toString.call() 作用是:
1、获取对象的类名(对象类型)。
2、然后将[object、获取的类名]组合并返回。
可应用于判断Array,Date,Function等类型的对象
*/
var num = new Number(1);
var arr = [1,2,3];
alert(Object.prototype.toString.call(num)); // [object Number]
alert(Object.prototype.toString.call(arr)); // [object Array]
// 扩展示例:(apply等价于call)
window.utils = {
toString: Object.prototype.toString,
isObject: function (obj) {
return this.toString.apply(obj) === '[object Object]';
},
isFunction: function (obj) {
return this.toString.apply(obj) === '[object Function]';
},
isArray: function (obj) {
return this.toString.apply(obj) === '[object Array]';
}
}
function A() { }
window.utils.isFunction(A); //true
window.utils.isObject(new A()); //true
window.utils.isArray([]); //true
/*
jQuery等框架 就是用这个方法判断对象的类型的,因此可以把这种方法作为权威的判断方法。
但是,如果重写了Object.prototype.toString方法,这时候再用来判断数据类型可能就会出错,
所以,一般不要去重写Object.prototype.toString方法。
*/
/********** 三:继承 **********/
/*
JS继承和闭包一样,几乎是每个想深入学习JS的朋友都要讨论的问题,因此各种相关资料应有尽有。
JS继承代码的版本非常多,但原理都是一样的,核心都是利用了prototype对象。
为了和其他面向对象语言的风格相似,大多数都采用“类式”风格模拟。
继承的详细原理不再赘述,网上有许多资料介绍。
这里给出一个示例:Jquery作者John Resig写的继承。
(其中的详细注释是来自某个博客,不知道是谁原创,这里私自转帖出来)
*/
(function () {
// initializing变量用来标示当前是否处于类的创建阶段,
// - 在类的创建阶段是不能调用原型方法init的
// - 我们曾在本系列的第三篇文章中详细阐述了这个问题
// fnTest是一个正则表达式,可能的取值为(/\b_super\b/ 或 /.*/)
// - 对 /xyz/.test(function() { xyz; }) 的测试是为了检测浏览器是否支持test参数为函数的情况
// - 不过我对IE7.0,Chrome2.0,FF3.5进行了测试,此测试都返回true。
// - 所以我想这样对fnTest赋值大部分情况下也是对的:fnTest = /\b_super\b/;
var initializing = false, fnTest = /xyz/.test(function () { xyz; }) ? /\b_super\b/ : /.*/;
// 基类构造函数
// 这里的this是window,所以这整段代码就向外界开辟了一扇窗户 - window.Class
this.Class = function () { };
// 继承方法定义
Class.extend = function (prop) {
// 这个地方很是迷惑人,还记得我在本系列的第二篇文章中提到的么
// - this具体指向什么不是定义时能决定的,而是要看此函数是怎么被调用的
// - 我们已经知道extend肯定是作为方法调用的,而不是作为构造函数
// - 所以这里this指向的不是Object,而是Function(即是Class),那么this.prototype就是父类的原型对象
// - 注意:_super指向父类的原型对象,我们会在后面的代码中多次碰见这个变量
var _super = this.prototype;
// 通过将子类的原型指向父类的一个实例对象来完成继承
// - 注意:this是基类构造函数(即是Class)
initializing = true;
var prototype = new this();
initializing = false;
// 我觉得这段代码是经过作者优化过的,所以读起来非常生硬,我会在后面详解
for (var name in prop) {
prototype[name] = typeof prop[name] == "function" &&
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
(function (name, fn) {
return function () {
var tmp = this._super; // 这里是必要的,第91行注释代码可说明之。
this._super = _super[name];
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]) :
prop[name];
}
// 这个地方可以看出,Resig很会伪装哦
// - 使用一个同名的局部变量来覆盖全局变量,很是迷惑人
// - 如果你觉得拗口的话,完全可以使用另外一个名字,比如function F()来代替function Class()
// - 注意:这里的Class不是在最外层定义的那个基类构造函数
// 这里的Class和上边的window.Class函数不一样,这里是window.Class内部的函数局部变量
function Class() {
// 在类的实例化时,调用原型方法init
if (!initializing && this.init)
this.init.apply(this, arguments);
}
// 子类的prototype指向父类的实例(完成继承的关键)
Class.prototype = prototype; // Class指代上边的Class,并非一开始的window.Class
// 修正constructor指向错误
// 是否可用Class.prototype.constructor = Class;来修正???
Class.constructor = Class;
// 子类自动获取extend方法,arguments.callee指向当前正在执行的函数
Class.extend = arguments.callee;
return Class;
};
})();

您可能感兴趣的文章:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wdjfdg.html