1.提出原型
创建对象的构造函数方法:
<script>
function Person(name,age,gender){
this.name = name;
this.age = age;
this.gender = gender;
this.say = function(content){
console.log(content);
}
}
var p1 = new Person("tom",4,"male");
var p2 = new Person("jerry",3,"male");
console.log(p1);
console.log(p2);
console.log(p1.say === p2.say);
</script>
p1和p2指向的不是同一个函数体,调用多次会非常复杂。
解决方法是把函数抽取到外面。
<script>
function Person(name,age,gender){
this.name = name;
this.age = age;
this.gender = gender;
this.say = fn;
}
function fn(content){
console.log(content);
}
var p1 = new Person("tom",4,"male");
var p2 = new Person("jerry",3,"male");
console.log(p1.say === p2.say);
</script>
但是,fn是全局函数,会造成全局污染,可以用原型解决。
2.关键点
每一个函数在定义的时候,都会有跟它关联的一个对象被创建出来。
每一个由构造函数创建出来的对象,都会默认的和构造函数的神秘对象关联。
当使用一个方法进行属性或者方法访问的时候,会先在当前对象内查找该属性和方法。
如果当前对象未找到,就回去跟它关联的神秘对象内进行查找。
获取神秘对象:构造函数的名字.prototype
<script>
function Person(name,age,gender){
this.name = name;
this.age = age;
this.gender = gender;
this.say = function(content){
console.log(content);
}
}
Person.prototype.sayHi = function(){
console.log("hi......");
}
var p1 = new Person("tom",4,"male");
var p2 = new Person("jerry",3,"male");
p1.sayHi();
console.log(p1.say === p2.say);
console.log(p1.sayHi === p2.sayHi);
</script>
3.原型属性和查找方法
<script>
function Person(){};
Person.prototype.name = "tom";
var p1 = new Person();
console.log(p1.name);
</script>
<script>
function Person(){};
Person.prototype.name = "tom";
var p1 = new Person();
p1.name = "jerry";
console.log(p1.name);
</script>
在访问的时候,如果没有该属性就到构造函数的原型中找
<script>
function Person(){
this.say = function(){
console.log("构造函数中的say");
}
}
Person.prototype.say = function(){
console.log("原型中的say");
}
var p1 = new Person();
p1.say();
</script>
4.原型的使用
<script>
function Person(){};
Person.prototype.say = function(){
console.log("say hello!");
}
console.log(Person.prototype);
var obj = {
say:function(){
console.log("hello!");
},
sayHi:function(){
console.log("hi!hi!");
}
}
Person.prototype = obj;
console.log(Person.prototype);
</script>
对比两次输出的Person.prototype,不同点在于constructor(告诉当前的原型是属于哪一个构造的)
手动维护,如果将构造函数的原型替换为其他对象的话,一定要加上constructor属性指明当前原型的构造是谁
Person.prototype.constuctor = obj;
或者把后来定义的say,sayHi添加到原型上。
<script>
function Person(){};
Person.prototype.say = function(){
console.log("say hello!");
}
console.log(Person.prototype);
var obj = {
say:function(){
console.log("hello!");
},
sayHi:function(){
console.log("hi!hi!");
}
}
for(var k in obj){
Person.prototype[k] = obj[k];
}
console.log(Person.prototype);
</script>
然后调用say方法
var p = new Person();
p.say();
后面的say方法把前面的覆盖掉了。
5.原型继承
如果想继承另一个对象里的方法,(举的例子是obj)那么就要写很多遍for in,所以,可以创建一个方法,把要继承的对象传进去,然后调用这个方法。
<script>
var Animal = {
name:"qq",
age:5,
gender:"male",
say:function(){
console.log("wang!");
}
}
function Dog(){
this.color = "black";
}
Dog.prototype.extend = function(obj){
for(var k in obj){
this[k] = obj[k];
}
}
Dog.prototype.extend(Animal);
console.log(Dog.prototype);
</script>
6.原型链
实例对象 -> 构造函数.原型 -> 原型的原型 -> ... -> Object.prototype -> undefined | xxx is not a function
function Animal(){
this.name = "animal",
this.say = function(){
console.log("Animal is animal");
}
}
Person.prototype = new Animal();
Person.prototype.constructor = Person;
function Person(){
this.name = "person",
this.say = function(){
console.log("Person is person");
}
}
Student.prototype = new Person();
Student.prototype.constructor = Student;
function Student(){
this.name = "student",
this.say = function(){
console.log("Student is a student");
}
}
Teacher.prototype = new Student();
Teacher.prototype.constructor = Teacher;
function Teacher(){
this.name = "teacher",
this.say = function(){
console.log("Teacher is a teacher");
}
}
console.log(Teacher.prototype);
7.Object
console.log(Object.prototype);
看一下Object的原型了解这几个方法。
1.hasOwnProperty 判断对象本身(不包括原型)是否拥有某个属性。
<script>
function Person(){
this.name = "lucy",
this.age = 19,
this.say = function(){
console.log("I'm Lucy.");
}
}
Person.prototype.gender = "female";
var p = new Person();
console.log(p.hasOwnProperty("name"));
console.log(p.hasOwnProperty("gender"));
</script>
2.propertyIsEnumerable 判断属性是否属于对象本身,判断属性是否可以遍历
Object.defineProperty 添加某个属性的时候可以附加一些信息
<script>
function Person(){
this.name = "lucy",
this.age = 19,
this.say = function(){
console.log("I'm Lucy.");
}
}
Person.prototype.gender = "female";
var p = new Person();
Object.defineProperty(p,"name",{
value:"tom",
//控制当前属性是否能被遍历
enumerable:false,
//是否可以写
writable:false,
//控制当前属性是否可以被删除
configurable:false
})
console.log(p.propertyIsEnumerable("name"));
console.log(p.propertyIsEnumerable("gender"));
</script>
set,get方法
<script>
function Person(){
this.name = "lucy",
this.age = 19,
this.say = function(){
console.log("I'm Lucy.");
}
}
Person.prototype.gender = "female";
var p = new Person();
Object.defineProperty(p,"age",{
get:function(){
console.log("在获取age属性的时候会自动调用此方法");
try {
return initValue;
} catch (error) {
return undefined
}
},
set:function(value){
if(value < 0 || value > 130){
console.log("年纪不符合逻辑");
return initValue = "超岁数了";
}
initValue = value;
}
})
p.age = -14;
console.log(p.age);
</script>
p.age = 14;
console.log(p.age);
8.举例
1.
请补全JavaScript代码,实现以下功能:
1. 给"Human"构造函数的原型对象添加"getName"方法,返回当前实例"name"属性
2. 将"Chinese"构造函数继承于"Human"构造函数
3. 给"Chinese"构造函数的原型对象添加"getAge"方法,返回当前实例"age"属性
<script type="text/javascript">
function Human(name) {
this.name = name
this.kingdom = 'animal'
this.color = ['yellow', 'white', 'brown', 'black']
}
function Chinese(name,age) {
Human.call(this,name)
this.age = age
this.color = 'yellow'
}
// 补全代码
Human.prototype.getName = function(){
return this.name;
}
Chinese.prototype = new Human();
Chinese.prototype.constructor = Chinese;
Chinese.prototype.getAge = function(){
return this.age;
}
</script>
2.
请补全JavaScript代码,要求在Number对象的原型对象上添加"_isPrime"函数,该函数判断调用的对象是否为一个质数,是则返回true,否则返回false。
<script type="text/javascript">
// 补全代码
Number.prototype._isPrime = function(num){
for(let i = 2; i < num; i++){
if(num % i == 0){
return false
}
}
return true
}
</script>