原型
- 定义:原型是function对象的一个属性,它定义了构造函数制造出对象的公共祖先。通过该构造函数产生的对象可以继承该原型的属性和方法。原型也是对象。
Person.prototype.name = "lijing";
Person.prototype.say = function () {
console.log("prototype:say");
}
function Person() {
}
var peo1 = new Person();
console.log(Person.prototype); // {name: "lijing", say: ƒ, constructor: ƒ}
console.log(peo1.name); // lijing
peo1.say();// prototype:say
Person.prototype.name = "lijing";
Person.prototype.say = function () {
console.log("prototype:say");
}
function Person(name) {
this.name = name;
this.say = function(){
console.log("person:say");
}
}
var peo1 = new Person("xiaoming");
console.log(peo1.name); // xiaoming
peo1.say();//person:say
- 利用原型的特点和概念,可以提取共有属性。
// 将car的公共属性放在prototype中,在每次实例化
// 对象的时候不用重复执行赋值操作
Car.prototype.height = 1400;
Car.prototype.lang = 2900;
Car.prototype.carName = 'BWM';
// 或者这样写也可以
// Car.prototype = {
// height:1400,
// lang:2900,
// carName:'BMW'
// }
function Car(color, owner) {
this.owner = owner;
this.color = color;
}
var car = new Car('red', 'xiaonming');
var car1 = new Car('white', 'lijing');
console.log(car); // Car {owner: "xiaonming", color: "red"}
console.log(car1); // Car {owner: "lijing", color: "white"}
console.log(car.height); // 1400
// 原型的增删改查只能通过操作Car.prototype.属性 来操作属性
- 对象如何查看原型–>隐式属性__proto__
- 对象如何查看对象的构造函数–>constructor
function Car(name) {
this.name = name;
}
var c = new Car('xiaoming'); // Car {name: "xiaoming"}
console.log(c);
console.log(c.constructor);
// ƒ Car(name) {
// this.name = name;
// }
console.log(Car.prototype);
// constructor: ƒ Car(name)
// __proto__: Object
// 可以看出构造函数是prototype的一个属性,
// 所以只打印对象的时候不会打印构造函数,但
// 是可以用过对象.constructor 来访问构造函数
__proto__属性
当我们直接打印一个对象的时候:
function Person(){
}
var person = new Person();
console.log(person);
运行结果:
Person
__proto__:
constructor:f Person()
__proto__:Object
实际上,在对象创建的时候,对象内部有一个__proto__属性,指向Person.prototype,如下:
var person = {
__proto__:Person.prototype
}
例1:
Person.prototype.name = 'xiaoming';
function Person(){
}
var person = new Person();
Person.prototype.name = 'xiaohong';
console.log(person.name);// xiaohong
// 之所以能打印出xiaohong是因为,在打印之前修改了原型的name属性,原有的name被覆盖
例2
Person.prototype.name = 'xiaoming';
function Person(){
}
var person = new Person();
Person.prototype = {
name : 'xiaohong'
}
console.log(person.name);// xiaoming
上面的代码实际上是修改了Person.prototype的指向,令Person.prototype指向的地址发生了变化,但是__proto__指向的地址没有发生变化。相当于下面代码:
Person.prototype.name = 'xiaoming';
function Person() {
//var this = {__proto__:Person.prototype}
}
var person = new Person();
// Person.prototype = {
// constructor:founction Person(){};
// }
// Person.__proto__ = Person.prototype;
Person.prototype = {
name: 'xiaohong'
}
// 这里可以知道,Person.__proto__的指向一直没有发生变化
console.log(person.name); // xiaoming
但是如果在修改了Person.prototype之后再打印person.name的话就是xiaohong
Person.prototype.name = 'xiaoming';
function Person() {
//var this = {__proto__:Person.prototype}
}
Person.prototype = {
name: 'xiaohong'
}
var person = new Person();
// 因为在创建person对象的时候,才将__proto__属性
// 设置为Person.prototype,此时Person.prototype已经发生了变化
console.log(person.name); // xiaohong
原型链
- 原型是一个对象,一个对象又可以有一个原型,所以可以形成原型链
// 原型链的最上端是Object.prototype,例如:(可见,打印结果中没有__proto__属性)
console.log(Object.prototype);
// constructor: ƒ Object()
// hasOwnProperty: ƒ hasOwnProperty()
// isPrototypeOf: ƒ isPrototypeOf()
// propertyIsEnumerable: ƒ propertyIsEnumerable()
// toLocaleString: ƒ toLocaleString()
// toString: ƒ toString()
// valueOf: ƒ valueOf()
// __defineGetter__: ƒ __defineGetter__()
// __defineSetter__: ƒ __defineSetter__()
// __lookupGetter__: ƒ __lookupGetter__()
// __lookupSetter__: ƒ __lookupSetter__()
// get __proto__: ƒ __proto__()
// set __proto__: ƒ __proto__()
- 原型的修改
- 通过“子类”不可以覆盖性的修改原型,但是可以通过获取引用来修改原型。
Person.prototype.info = {
name: "xiaoming"
}
function Person() {
}
var p = new Person();
p.info = '信息';
console.log(p.info); // 信息
console.log(p); // Person {info: "信息"}
console.log(Person.prototype.info); // {name: "xiaoming"}
// 可见,在对象上直接修改只是给先有对象增加了一个属性,并没有修改原型
// 我们可以尝试获取对象的引用,在进行修改
Person.prototype.info = {
name: "xiaoming"
}
function Person() {
}
var p = new Person();
p.info.name = 'lijing';
console.log(p.info); // {name: "lijing"}
console.log(p); // Person {}
console.log(Person.prototype.info); // {name: "lijing"}
- this的含义
Person.prototype = {
name: 'a',
sayName: function () {
console.log(this.name);
}
}
function Person(){
this.name = 'b'
}
var person = new Person();
person.sayName();// b
Person.prototype.sayName();// a
// 因为当前是谁调用的sayName(),sayName中的this就指向谁
// 所以当person调用sayName()的时候,就输出Person类里面的name
// 当Person.prototype调用sayName()的时候,就调用Person.prototype的name
-
Object.create()方法
Object.create(pro)可以用来创建对象,参数pro为该对象的原型,如下:
var father = {
name:'lijing',
age:18
}
var obj = Object.create(father);
console.log(obj);
// {}
// __proto__:
// age: 18
// name: "lijing"
// __proto__: Object
// 注意:“javascript中所有对象都继承自Object.prototype”是假命题,
// 因为用var obj = Object.create(null);创建的对象,没有__proto__属性
var obj = Object.create(null);
console.log(obj);
// {}
// No properties
// 如果手动添加__proto__属性的话不会存在继承的效果
var obj = Object.create(null);
obj.__proto__ = {
name:'xiaoming'
}
var son = Object.create(obj);
console.log(son);
// {}
// __proto__:
// __proto__:
// name: "xiaoming"
// __proto__: Object
console.log(son.name);// undefined
- toString()方法
- 根据上面就可以解释null和undefined不能调用toString()方法的原因,因为null和undefined不是对象,就没有继承自Object.prototype,所以没有toString()方法,而数字和字符串会经过包装类包装成对象,就存在toString()方法了
- Number类型和Boolean类型等调用的toString()方法其实是在类中重写之后的toString()方法,如果不进行重写的话,调用的是Object.prototype中的toString()方法
- document.write()函数实际上是调用了参数的toString()方法
- toFixed()方法中的bug
- 原因在于javascript在处理小数的时候会精度不准
console.log(0.14 * 100);// 14.000000000000002
// 所以产生随机数的时候,一般不用toFixed(),而用取整
console.log(Math.floor(Math.random()* 100));// 59
console.log(Math.random().toFixed(2) * 100);// 28.999999999999996
call()/apply()
作用:改变this的指向
function Person(name,age){
this.name = name;
this.age = age;
}
var obj={};
Person.call(obj,'hong',18);
// 在执行该函数的时候将函数里面的this全部替换为obj,
// 然后后面的参数按照形参列表传递进去
console.log(obj);// {name: "hong", age: 18}
应用:实现代码的重用
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
function Student(name, age, sex, tel, grade) {
Person.call(this, name, age, sex);
// 相当于执行了Person构造函数里的方法
this.tel = tel;
this.grade = grade;
}
var student = new Student('xiaoming',18,'男',110,100);
console.log(student);
// Student {name: "xiaoming", age: 18, sex: "男", tel: 110, grade: 100}
区别:传参列表不同
call和apply的作用一样,但是call需要把实参按照实参的个数传进去,apply需要传一个arguments数组,上面的代码可以改为:Person.apply(this,[name,age,sex])
版权声明:本文为li3455277925原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。