原型
将函数定义在全局作用域,污染了全局作用域的命名空间,而且定义在全局作用域中也很不安全
原型(prototype):创建每一个的函数,解析器都会向函数中添加一个属性(prototype),这个属性对应着一个对象,这个对象就是所谓的原型对象
如果函数作为普通函数调用prototype没有任何作用;当函数作为构造函数调用时,它所创建的对象中都会有一个隐含的属性(_proto_),指向该构造函数的原型对象,我们可以通过__proto__来访问该属性。原型(prototype)指向一个对象(原型对象),所以原型对象也含有__proto__属性
构造函数的原型prototype和该实例对象的__proto__属性指向的是同一个对象(对象的__proto__属性指向构造它的构造函数的原型对象)
原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将一类对象中共有的属性和方法,统一设置到构造函数的原型对象中,这样就不会影响到全局作用域
函数中的是prototype属性,指向原型对象;对象中的是__proto__属性,也是指向原型对象(对象的__proto__属性指向它的构造函数的原型对象)
当我们访问对象的一个属性或方法时,它会先在对象自身寻找,如果有则直接使用;如果没有,则会去原型对象(prototype)中寻找,如果找到则直接使用;如果再没有就去原型对象的原型(__proto__)中寻找,如果原型中有,则使用;如果再没有则去原型的原型中寻找,直到找到Object对象的原型。Object对象的原型(__proto__)没有原型,如果在Object中仍然没有找到,则返回null
<script>
// 构造函数
function Person() {
}
// 向Person的原型中添加一个属性name
Person.prototype.name = "张三";
// 向Person的原型中添加一个方法
Person.prototype.sayHi = function() {
// 构造函数中的this指向它创建的实例对象
console.log(this.name);
};
// 创建Person的实例对象
var p1 = new Person();
var p2 = new Person();
p2.name = "李四";
// prototype原型
console.log(Person.prototype);
console.log(p1.__proto__);
console.log(Person.prototype == p1.__proto__); //true
console.log(p1.name); //p1自身没有name属性,则去原型找,name属性值是原型对象中的name属性值,值为张三
console.log(p2.name); //p2自身具有name属性,所以name属性是自身的属性,值为李四
p1.sayHi(); //p1里面没有方法,去原型里面找
// 构造函数的prototype属性的指向和它的实例对象的__proto__属性的指向的相同的,指向同一个对象
console.log(Person.prototype == p1.__proto__); //true
// 构造函数的prototype属性指向它的原型对象,原型对象也是对象,所以也拥有__proto__属性
console.log(Person.prototype.__proto__ == p1.__proto__.__proto__); //true
</script>
使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true
使用hasOwnProperty()方法检查对象中是否含有某个属性,只有对象自身存在该属性才会返回true
<script>
// 构造函数
function Person() {
}
// 向Person的原型中添加一个属性name
Person.prototype.name = "我是原型中的张三";
// 创建Person的实例对象
var p1 = new Person();
p1.age = 22;
// 检查p1中是否含有name属性
console.log("name" in Person); //true
console.log("name" in p1); //true
// 可以使用对象hasOwnProperty()方法来检查对象中是否含有该属性,只有对象自身中含有这个属性才会返回true
console.log(p1.hasOwnProperty("name")); //p1自身不含有这个属性,所以返回false
console.log(p1.hasOwnProperty("age")); //p1自身含有age属性,所以返回true
console.log(p1.hasOwnProperty('hasOwnProperty')); //false
console.log(p1.__proto__.hasOwnProperty('hasOwnProperty')); //false
// 说明hasOwnProperty方法是存在于对象的原型对象(p1.__proto__)的原型(__proto__)中
console.log(p1.__proto__.__proto__.hasOwnProperty('hasOwnProperty')); //true
// 对象的对象原型(__proto__属性)指向它的构造函数的原型(prototype属性),两个属性所指的对象是同一个对象
// 对象的对象原型__proto__指向原型对象(构造函数的原型prototype的指向),原型对象的原型__proto__指向它的构造函数的原型(构造函数的的原型对象的原型指向Object构造函数的原型对象)
// Object构造函数的原型对象的原型__proto__指向的原型对象不存在(Object是最高级了)
// 原型链
console.log(p1.__proto__.__proto__.__proto__); //null
</script>
toString方法产生的问题
<script>
// 创建构造函数
function Person(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
// 给构造函数的原型添加toString方法,由Person创建出来的对象,都会有这个toString方法
Person.prototype.toString = function() {
return "Person[name=" + this.name + ",age=" + this.age + ",gender=" + this.gender + "]";
};
// 创建一个Person实例
var p = new Person('zs', 22, '男');
var s = new Person('ls', 28, '男');
// 当我们直接在页面中打印一个对象时,实际上是输出的对象的toString()方法的返回值
// 如果不希望在输出对象时不输出[object Object],可以给对象添加一个toString()方法
// 单独给对象加toString()方法,这样只对该对象有效
/* p.toString = function() {
return "Person[name=" + this.name + ",age=" + this.age + ",gender=" + this.gender + "]";
}; */
console.log(p.toString());
console.log(s.toString());
// console.log(p);
// var res = p.toString();
// console.log('res:' + res); //res:[object Object]
// 这个toString方法是存放在Object的原型中
console.log(p.__proto__.__proto__.hasOwnProperty("toString")); //true
</script>
垃圾回收(垃圾收集)
当一个对象没有任何的变量或属性对它进行引用,此时我们将永远无法操作该对象,此时这种对象就是一个垃圾,这种对象就过多的占用大量的内存空间,导致程序运行变慢。
在js中拥有自动的垃圾回收的机制,会自动将这些垃圾对象从内存中销毁,我们不需要也不能进行垃圾回收的操作
我们需要手动将不再需要使用的对象设置为null即可
<script>
var obj = new Object();
// 一顿操作之后
obj = null;
// 我们只需要将obj设为null,剩下的就交给js的垃圾回收机制处理
</script>