js constructor 和prototype
深入理解js后这两个核心概念非常重要
我们在定义函数的时候,函数本身就会默认有一个prototype的属性,而我们如果用new运算符来生成一个对象的时候就没有prototpye属性。
!function a(){
varo = new Object();
function b(){
}
return
}()
var a = new a();
js 原型对象与原型链
原型对象
javascript 对象都有一个原型对象,在不同的解释器下的实现不同。比如firefox下,每个对象都有一个隐藏的__proto__属性,这就是
‘原型’对象的引用。
原型链
由于原型对象的本身也是对象,它也有自己的原型,而它的原型对象又可以有自己的原型,这样就组成了一条链,就是原型链,在访问对象的属性时,如果在对象本身中没有找到,则回去原型链中查找,如果找到返回值,否则返回Undefined
var base {
name : “base”,
getInfo : function (){
return this.name;
}
}
varext1 = {
id: 0,
__proto__ : base
}
var ext2 = {
id: 9,
__proto__ : base
}
print(ext1.id);
print(ext1.getInfo());
print(ext2.id);
print(ext2.getInfo());
输出:0
base
9
base
可以看到,如果自身找到了属性就回输出,如果找不到属性就回去子项的原型中去找
js中关键词 in 的使用方法:
除了 for .. in 循环数组或集合之外
in当‘对象’是数组时,‘变量’指引索引
当‘对象’是对象时,‘变量’指引属性
vararr =["a","b","2","3","str"];
"b"in arr
4 in arr
输出: false
true
varobj={
w:"wen",
j:"jian",
b:"bao"
}
2 in obj
"bao"in obj
输出: false
true
闭包的概念:
通过闭包可以使我们的代码更加优雅,更加简洁,在某些方面可以提升代码的效率
1.匿名自执行函数
变量如果不加var 会默认添加到全局变量中,这些临时变量如果误用会导致全局对象过于庞大,影响访问速度(因为变量取值是需要从原型链上取的)
var datamodel = {
table : [],
tree : {}
};
(function(dm){
for(var i = 0; i < dm.rows.length;i++){
var row = dm.rows[i];
for(var j = 0; j < row.cells.length;i++){
drawCell(i, j);
}
}
//build dm.tree
})(datamodel);
这个函数由于外部无法直接引用它的内部变量所以执行完后很快就会被释放
2.缓存
假设有一个很耗时间的函数对象,每次调用都会花费很多时间;
那么我们就需要将计算的值储存起来,当调用这个函数的时候,首先从缓存中取出,如果找不到再进行计算
闭包正是可以做到这一点,因为它不会释放外部引用,从而函数值得到保留
var CachedSearchBox = (function(){
var cache = {},
count = [];
return {
attachSearchBox : function(dsid){
if(dsid in cache) return cache[dsid];
var fsb = newuikit.webctrl.SearchBox(dsid);//新建
cache[dsid] = fsb;
return fsb;
},
clearSearchBox : function(dsid){
if(dsid in cache)
cache[dsid].clearSelection();
}
}
})
3.实现封装
var person = function(){
var name = "default";
return {
getName : function (){
return name;
},
setName : function (newName){
name = newName;
}
}
}();
print(person.name)//undefined
print(person.getName())//default
person.setName("hello");
print(person.getName())//hello
4.闭包的另一个重要用途是实现面向对象中的对象,这个对象拥有独立的成员及状态,互不干涉。
functionPerson(){
var name = "default";
return {
getName : function(){
return name;
},
setName : function(newName){
name = newName;
}
}
};
varjohn = Person();
print(john.getName());
john.setName("john");
print(john.getName());
varjack = Person();
print(jack.getName());
jack.setName("jack");
print(jack.getName());
由此可见,john和jack都可以成为Person的实例,因为两个实例对name这个成员的访问时独立的。
Js caller,callee,call,apply 概念
Arguments :
该对象代表正在执行的函数和调用它的函数的参数
arguments 是一个类似数组但不是数组的对象
arguments.length 是实参的长度arguments.callee.length是形参的长度
1 Array.prototype.selfvalue = 1;
2 alert(new Array().selfvalue);
3 function testAguments(){
4 alert(arguments.selfvalue);
5 }
输出为:undefined
caller :
返回一个函数的引用,该函数调用了当前函数
functionName.caller
1 // caller demo {
2 function callerDemo() {
3 if (callerDemo.caller) {
4 var a= callerDemo.caller.toString();
5 alert(a);
6 } else {
7 alert("this is a top function");
8 }
9 }
10 function handleCaller() {
11 callerDemo();
12 }
caller只有函数执行时才有定义。如果是函数顶层调用则为null
callee :
返回正在被执行的function对象,也就是function的正文
callee是arguments对象的一个成员,它表示函数对象的本身。
使用场景:有利于匿名函数的递归和保证函数的封装性
1 //用于验证参数
2 function calleeLengthDemo(arg1, arg2) {
3 if (arguments.length==arguments.callee.length) {
4 window.alert("验证形参和实参长度正确!");
5 return;
6 } else {
7 alert("实参长度:" +arguments.length);
8 alert("形参长度: " +arguments.callee.length);
9 }
10 }
11 //递归计算
12 var sum = function(n){
13 if (n <= 0)
14 return 1;
15 else
16 return n +arguments.callee(n - 1)
17 }
18 //比较一般的递归函数
19
20 var sum = function(n){
21 if (1==n) return 1;
22 else return n + sum (n-1);
apply 和call :
他们的作用就是将函数绑定到另一个对象上去运行,两者仅定义参数的方式区别:
apply(thisArg,argArray); call(thisArg[,arg1,arg2]);
将所有函数内部的this指针都会被赋值为thisArg,这可实现将函数作为另外一个对象方法运行的目的
值得提的一点,在javascript框架prototype里就是使用apply来创建一个定义类的模式
1 var Class = {
2 create: function() {
3 return function() {
4 this.initialize.apply(this, arguments);
5 }
6 }
7 }
封装之弊
封装会影响作用域问题,使调试困难,一般来说不算大问题,但有时会很难区分来自不同作用域的同名变量,这是实现私有方法和属性所需的闭包会让它变得复杂
掺和对象
function mixin(dest,src) {
for (var key in src){
if (!dest[key]){
dest[key]= src[key]
}
}
}
var person= {name: 'John', age: 29}
var obj ={name: 'Jack'}
mixin(obj, person)
console.log(obj) //Object { name="Jack", age=29}
改变成更加灵活的Mixin,比如可以将任意多的参数掺和到对象中
function mixin(dest /*,Any number of objects */) {
var sources= Array.prototype.slice.call(arguments, 1)
for (var i=0;i<sources.length; i++) {
var src= sources[i]
for (var key in src){
if (!dest[key]){
dest[key]= src[key]
}
}
}
}
var person= {name: 'John', age: 29, toString: function(){return 'aa'}}
var permission= {open: 1}
var obj= {name: 'Jack'}
mixin(obj,person, permission)
console.log(obj) //Object { name="Jack", age=29, open=1}
掺和类
因为构造器只有一个原型对象,不过这可以通过多个掺元类的方式实现扩充,算是一种变相的多继承。
function augment(destClass, /*,Any number of classes */) {
var classes= Array.prototype.slice.call(arguments, 1)
for (var i=0;i<classes.length; i++) {
var srcClass= classes[i]
var srcProto = srcClass.prototype
var destProto= destClass.prototype
for (var method in srcProto){
if (!destProto[method]){
destProto[method]= srcProto[method]
}
}
}
}
指定拷贝方法
function augment(destClass,srcClass, methods) {
var srcProto = srcClass.prototype
var destProto= destClass.prototype
for (var i=0;i<methods.length; i++) {
var method= methods[i]
if (!destProto[method]){
destProto[method]= srcProto[method]
}
}
}
function Person(){}
Person.prototype.getName= function() {}
Person.prototype.setName = function() {}
Person.prototype.getAge = function() {}
Person.prototype.setAge = function() {}
function Student(){}
augment(Student,Person, ['getName', 'setName'])
var s1= new Student()
console.log(s1) //Student { getName=function(), setName=function()}