最近再次拜读javascript高级程序设计函数部分,发现一个问题。所以这里拿出来分享一下。书中提到下面两个表达式输出结果应该分别为20和TypeError,因为函数声明会涉及到函数声明提升的影响,使其在执行任何代码之前可用。函数表达式必须等到解析器执行到其所在的代码行才开始执行。
console.log(params(10, 10));
function params(num1, num2){
return num1+num2;
}
//20
console.log(params(10, 10));
var params= function(num1, num2){
return num1+num2;
};
//TypeError: params is not a function.
于是在浏览器中试验得到了一个吃惊的结果。
上面的情况在safari中完全满足,Version 9.1.1;
如果把console.log,改成alert, Safari中的结果又正常了。
alert(params(10, 10));
var params= function(num1, num2){
return num1+num2;
};
//20
但是在chrome和firefox中都会输出20,chrome Version 50.0.2661.94; firefox Version 46.0.1;
构造函数相关:
function Person(name, age, sex){
this.name= name;
this.age= age;
this.sex= sex;
this.sayName= function(){
console.log(this.name);
};
}
var person1= Person("fred", "18", "man"); //直接调用函数把属性方法添加到了window对象上。
person1.sayName() //Uncaught TypeError: Cannot read property 'sayName' of undefined(…)
function Person(name, age, sex){
this.name= name;
this.age= age;
this.sex= sex;
this.sayName= function(){
console.log(this.name);
};
}
var person1= Person("fred", "18", "man");
window.sayName(); //fred
function Person(name, age, sex){
this.name= name;
this.age= age;
this.sex= sex;
this.sayName= function(){
console.log(this.name);
};
}
var o={};
var person1= Person.call(o,"fred", "18", "man");
o.sayName() //fred 通过call方法定义一个对象作为接受者。
function Person(name, age, sex){
this.name= name;
this.age= age;
this.sex= sex;
this.sayName= function(){
console.log(this.name);
};
}
var person1= new Person("fred", "18", "man");
var person2= new Person("helen", "18", "female");
person1.sayName === person2.sayName //false 两个方法不是同一个构造函数的实例。
改造方法是把函数的定义移到构造函数的外面。把sayName属性设置成等于全局的sayName函数,这样一来,sayName包含的是指向函数的指针,因此person1和person2共享了在全局作用域中定义的同一个sayName函数,这样做确实解决了,两个函数做一件事情的问题,但是新的问题是,在全局作用域中定义的函数,仅仅用到了这里,有点浪费啊,而且定义很多方法的时候意味着定义很多全局变量,毫无封装性可言。
function Person(name, age, sex){
this.name= name;
this.age= age;
this.sex= sex;
this.sayName= sayName;
}
function sayName(){
console.log(this.name);
};
var person1= new Person("fred", "18", "man");
//实例化实质是新建一个对象,把构造函数的属性方法以及作用域复制到实例函数中,再返回实例函数。
var person2= new Person("helen", "18", "female");
person1.sayName === person2.sayName //true
再次改造的方法是通过在原型上定义方法。
function Person(){}; Person.prototype.name= “fred”; Person.prototype.age= “28”; Person.prototype.sex= “male”; Person.prototype.sayName=function(){ return this.name; } var person1= new Person(); var person2= new Person(); person1 === person2;
遍历对象属性各个方法的区别。
(1) for … in; Object.getOwnPropertyNames(); object.hasOwnProperty(); Object.keys()
function Person(){};
Person.prototype.name= "fred";
Person.prototype.age= "28";
Person.prototype.sex= "male";
Person.prototype.sayName=function(){
return this.name;
}
var person1= new Person();
person1.color= "yellow";
person1.look= function(){
return "what are you look";
}
var array1= [];
for (var prop in Person.prototype){
array1.push(prop);
}
console.log(array1); //
["name", "age", "sex", "sayName"] for ... in遍历对象的可枚举属性。
console.log(person1.hasOwnProperty("name")); //false
console.log(Person.prototype.hasOwnProperty("constructor"));
//true 获得对象上可枚举和不可枚举的所有实例属性。
console.log(Object.getOwnPropertyNames(person1)); //["color", "look"]
console.log(Object.getOwnPropertyNames(Person.prototype)); //["constructor", "name", "age", "sex", "sayName"] 获得对象上可枚举和不可枚举的所有实例属性。
console.log(Object.keys(Person.prototype));
// ["name", "age", "sex", "sayName"] 获取对象上的所有可枚举属性。
(2) in, hasOwnProperty在实例中的应用; in可以访问实例上和原型上定义的属性;hasOwnProperty只能访问实例上的属性。
function Person(){};
Person.prototype.name= "fred";
Person.prototype.age= "28";
Person.prototype.sex= "male";
Person.prototype.sayName=function(){
return this.name;
}
var person1= new Person();
person1.color= "red";
function hasPrototypeProperty(obj, name){
return !obj.hasOwnProperty(name) && (name in obj);
}
console.log(hasPrototypeProperty(person1, "color")); //false;
console.log(hasPrototypeProperty(person1, "name")); //true;
论prototype对象字面量写法,和赋值写法的差异。
function Person(){};
Person.prototype= {
name: "fred",
age: "18",
sex: "male",
sayName: function(){
return this.name;
}
};
var person1= new Person();
person1.color= "red";
function hasPrototypeProperty(obj, name){
return !obj.hasOwnProperty(name) && (name in obj);
}
console.log(person1.constructor === Person); //false ,继承了原型的constructor属性,原型的constructor属性指向了Object,而不是Person。
console.log(person1.constructor === Object); //true
console.log(person1.prototype); //undefined; 因为重写原型切断了原型和原来创建的实例的联系。
console.log(Object.getOwnPropertyNames(Person.prototype));
// ["name", "age", "sex", "sayName"]
console.log(Person.prototype.constructor === Person); //false 因为对象字面量相当于把原型对象重写了,因此constructor属性也就变成了新对象的constructor属性,指向Object。
function Person(){};
Person.prototype.name= "fred";
Person.prototype.age= "28";
Person.prototype.sex= "male";
Person.prototype.sayName=function(){
return this.name;
}
var person1= new Person();
console.log(person1.constructor === Person);
console.log(person1.prototype); //undefined; ???????
console.log(Object.getOwnPropertyNames(Person.prototype));
//["constructor", "name", "age", "sex", "sayName"]
console.log(Person.prototype.constructor === Person); //true;
原型的问题:原型中的属性被所有的实例共享,这种共享对于函数非常合适,对于包含基本值的属性,通过覆盖原型属性也可以实现,但是对于应用类型的例如数组的话,该一个实例的属性,原型也跟着变化,紧接着所有实例都变化。
function Person(){};
Person.prototype= {
constructor: Person,
name: ["fred", "paul"],
age: "18",
sex: "male",
sayName: function(){
return this.name;
}
};
var person1= new Person();
var person2= new Person();
person1.name.push("helen");
console.log(person1.name); //["fred", "paul", "helen"]
console.log(person1.name === person2.name); //true
改善1:组合使用构造函数模式和原型模式
function Person(age, sex){
this.name= ["fred", "paul"],
this.age= age,
this.sex= sex
};
Person.prototype= {
constructor: Person,
sayName: function(){
return this.name;
}
};
var person1= new Person("18","male");
var person2= new Person("20","female");
person1.name.push("helen");
console.log(person1.name); //["fred", "paul", "helen"]
console.log(person2.name); //["fred", "paul"]
console.log(person1.sayName()); //["fred", "paul", "helen"]
console.log(person2.sayName()); //["fred", "paul"]
console.log(person1.sayName === person2.sayName); //true;
改善2:动态原型创建法
function Person(age, sex){
this.name= ["fred", "paul"],
this.age= age,
this.sex= sex
if (typeof this.sayName != "function"){
Person.prototype.sayName= function(){
console.log(this.name);
}
}
};
var person1= new Person("18","male");
var person2= new Person("20","female");
person1.name.push("helen");
person1.sayName(); //["fred", "paul", "helen"]
person2.sayName(); //["fred", "paul"]
改善3:寄生构造模式
function SpecialArray(){
var values= new Array();
values.push.apply(values, arguments);
values.toPipeValue= function(){
return this.join("|");
}
return values;
}
var array1= new SpecialArray("14", "29", "40");
console.log(array1.toPipeValue()); //14|29|40