目录
- 1,构造函数
- 2,原型
- 3,this
- 4,原型链
- 1,特点
- 2,Object.prototype.toString()
- 3,instanceof 运算符
- 4,Object.getPrototypeOf()
- 5,创建空原型对象
- 6,面试题
- 5,继承
- 封装继承
1,构造函数
1,作用:创建对象。
通过普通函数返回对象:
function createPerson(firstName, lastName) {
var obj = {}
obj.firstName = firstName
obj.lastName = lastName
obj.fullName = firstName + lastName
obj.myName = function () {
console.log(obj.fullName)
}
return obj
}
const person = createPerson('张', '三')
通过构造函数创建对象
function Person(firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
this.fullName = firstName + lastName
this.myName = function () {
console.log(this.fullName)
}
}
const person = new Person('张', '三') // 规定语法
2,js 中所有的对象,都是通过构造函数创建的。
Object() 构造函数,所以 typeof Object === 'function'
var obj = {
age: 18
}
// 上面是语法糖
var obj = new Object()
obj.age = 18
Function()构造函数
每个 js 函数实际上都是一个 Function 对象,通过 Function
构造函数创建时(可以不加 new
关键字)。
const add= new Function("a", "b", "return a + b");
add(2, 6);
2,原型
当构造函数中定义方法时,因为每个构造出的实例都是独立的,所以每次也会创建一个新的方法。
function Foo(a, b) {
this.a = a
this.b = b
this.sayHi = function () {
console.log(this.a + this.b)
}
}
const foo1 = new Foo(1, 2)
const foo2 = new Foo(1, 2)
console.log(foo1.sayHi === foo2.sayHi) // false
这会造成内存空间的浪费,所以出现了原型来解决这个问题。
当访问实例成员时,先找自身,如果不存在,会自动从隐式原型中寻找。
原型也是一个对象,一般会将公共的方法放到原型上。
function Foo(a, b) {
this.a = a
this.b = b
}
Foo.prototype.sayHi = function () {
console.log(this.a + this.b)
}
const foo1 = new Foo(1, 2)
const foo2 = new Foo(1, 2)
console.log(foo1.sayHi === foo2.sayHi) // true
3,this
1,在函数中使用 this,它的指向完全取决于函数是如何调用的。具体参考
例子,为所有对象添加方法 print,并打印对象的键值对。
Object.prototype.print = function () {
for (var key in this) {
// 过滤掉 print
if (this.hasOwnProperty(key)) {
console.log(key, this[key])
}
}
}
var obj = {
a: 1,
b: 2
}
console.log(obj.print())
2,hasOwnProperty
表示对象自有属性(而不是继承来的属性)中是否具有指定的属性。
3,in 运算符
判断属性名是否在对象自身及其原型链上。
4,原型链
1,特点
-
在
Object.prototype
上添加属性,会影响到所有对象,包括函数和数组。 -
在
Function.prototype
上添加属性,会有影响到所有函数,所以可以判断一个变量是不是函数。
Function.prototype.isFun = true
var obj = {}
function fun() {}
console.log(obj.isFun) // undefined
console.log(fun.isFun) // true
2,Object.prototype.toString()
MDN - Object.prototype.toString()
会把对象转为字符串格式 [object Object]
,但这种格式并不是每个对象想要的。
所以数组重写了 toString
方法
Array.prototype.toString === Object.prototype.toString // false
如何让数组的 toString 方法使用 Object.prototype.toString
const arr = [1,2,3]
Object.prototype.toString.call(arr)
所以判断一个变量是不是数组的方法:
const arr = [];
console.log(Object.prototype.toString.call(arr)); // [object Array]
如果自己的构造函数希望改变 toString,如何改变?
function User() {}
const user = new User();
User.prototype.toString = function () {
return "xxx";
};
console.log(user.toString()); // xxx
3,instanceof 运算符
MDN - instanceof 运算符
用于检测构造函数的 prototype,是否出现在某个实例对象的原型链上。
function User() {}
var user = new User()
console.log(user instanceof User) // true
console.log(user instanceof Object) // true
o in C
更多的用来判断对象 o
是不是构造函数 C
的创建的实例。
所以,可以这样理解:
obj instanceof Array // obj 是不是数组
obj instanceof Object // obj 是不是对象
obj instanceof Function // obj 是不是函数
4,Object.getPrototypeOf()
Object.getPrototypeOf()
因为不推荐使用 __proto__
,所以有了新的方法来操作隐式原型:Object.getPrototypeOf()
和 Object.setPrototypeOf()
const arr = [];
console.log(Object.getPrototypeOf(arr) === arr.__proto__); // true
5,创建空原型对象
方法1,
const obj = {}
obj.__proto__ = null // 不推荐 直接操作 __proto__
Object.setPrototypeOf(obj, null) // 可以使用
方式2,
const obj = Object.create(null)
6,面试题
下面的面试题,都可以用上面的原型链图快速解释。
// 下面的代码输出什么?(京东)
Function.prototype.a = 1;
Object.prototype.b = 2;
function A() {}
var a = new A();
console.log(a.a, a.b); // undefined 2
console.log(A.a, A.b); // 1 2
// 下面的代码输出什么?(字节)
console.log({} instanceof Object); // true
console.log({}.toString instanceof Function); // true
console.log(Object instanceof Function); // true
console.log(Function instanceof Object); // true
// 下面的代码输出什么?
function User() {}
User.prototype.sayHello = function () {};
var u1 = new User();
var u2 = new User();
console.log(u1.sayHello === u2.sayHello); // true
console.log(User.prototype === Function.prototype); // false
console.log(User.__proto__ === Function.prototype); // true
console.log(User.__proto__ === Function.__proto__); // true
console.log(u1.__proto__ === u2.__proto__); // true
console.log(u1.__proto__ === User.__proto__); // false
console.log(Function.__proto__ === Object.__proto__); // true
console.log(Function.prototype.__proto__ === Object.prototype.__proto__); // false
console.log(Function.prototype.__proto__ === Object.prototype); // true
5,继承
假设有一个会员系统
- 免费用户
- 属性:用户名,密码
- 方法:播放免费视频
- vip 用户
- 属性:还有会员过期时间
- 方法:还有播放vip视频
function User(name, pwd) {
this.name = name
this.pwd = pwd
}
User.prototype.playFreeVideo = function() {
console.log('免费视频');
}
function VipUser(name, pwd, expires) {
this.name = name
this.pwd = pwd
this.expires = expires
}
VipUser.prototype.playFreeVideo = function() {
console.log('免费视频');
}
VipUser.prototype.playVipVideo = function() {
console.log('vip视频');
}
可以看到有冗余的代码。改造下
1,处理构造函数内部的重复。
function VipUser(name, pwd, expires) {
User.call(this, name, pwd)
this.expires = expires
}
2,处理原型上的重复
需要一个图来表现关系
// VipUser.prototype.__proto__ = User.prototype
Object.setPrototypeOf(VipUser.prototype, User.prototype)
继承就是上面这种关系。
- 子类的实例,应该自动拥有父类的所有成员。
- 子类最多只有一个父类
- 间接父类的成员,会传递给子类
封装继承
function inherit(Child, Parent) {
Object.setPrototypeOf(Child.prototype, Parent.Parent)
}