创建对象的几种方式(类)
构造函数模式
function Person(name,gender,age){
this.name = name;
this.gender = gender;
this.age = age;
this.sayName = function(){
console.log("name is " + this.name)
}
}
let tom = new Person("tom","man",18)
工厂函数模式
function Person(name,gender,age){
let obj = new Object()
obj.name = name;
obj.gender = gender;
obj.age = age;
obj.sayName = function(){
console.log("name is " + obj.name)
}
return obj;
}
let tom = Person("tom","man",18)
工厂函数和构造函数区别
1.构造函数没有明显的创建对象过程
这一点是最本质的区别,工厂模式会创建不同的继承自Object的对象,而构造函数模式不会
2.没有return
3.书写风格上首字母大写
new关键字的作用
1.创建一个新的对象obj
2.将构造函数的prototype原型作为新创建对象的proto原型
3.执行构造函数并且绑定this为新创建对象
4.构造函数执行结果是对像则返回执行结果否则返回新创建对象
手写new的pollyfill
function myNew(fn,...rest){
let obj = Object.create(fn.prototype);
const res = fn.apply(obj,rest);
return typeof res === 'object' ? res : obj;
}
class模式
class模式是es6引入的方法
class Person {
constructor(name,gender,age){
this.name = name;
this.gender = gender;
this.age = age;
}
sayName(){
console.log("name is " + this.name)
}
}
let tom = Person("tom","man",18)
class模式还提供了get方法,set方法,static静态方法
class Person {
constructor(name,gender,age){
this.name = name;
this.gender = gender;
this.age = age;
}
//获取某一个属性时的改写方法
get name(){
return this.name+"getter"
}
//设置某一值时的改写方法
set name(val){
return this.name+"setter"+val
}
//静态方法,只能被类调用,不能被实例调用
static walk(){
return "he is walk"
}
sayName(){
console.log("name is " + this.name)
}
}
let tom = Person("tom","man",18)
对象继承的几种方式
对象继承的原理
对象的继承简单来说就是一个对象可以使用其父对象的属性和方法,和其最相关的知识点就是原型链的概念
原型链
原型链包含了几个不同的小概念:
- 构造函数
构造函数就是用于创建对象的函数,其包含一个属性为“prototype”,即构造函数的原型- 实例对象的原型
这个就是我们Object.getprototypeof和Object.setprototypeof所得出的原型对象,在浏览器中多表述为_proto_- 这二者的关系
简单来说就是对象的__proto__等于构造函数的prootype- 原型链
因为此种对等关系,在a对象使用过程中,如果其构造函数的prototype为b对象的实例,则a继承b对象,在a中可以访问继承而来的属性,从而形成的层层继承链条(在js中,顶层原型为null)
继承的方式
- 原型继承
//子对象构造函数
function SubType(){
this.name = "sub"
}
//父对象构造函数
function SuperType(){
this.name = "super"
}
//继承(使构造函数的prototype等于父对象实例)
SubType.prototype = new SuperType()
- 构造函数模式
//父对象构造函数
function SuperType(name){
this.man = name
}
//子对象构造函数
function SubType(){
SuperType.call(this,"super")
this.name = "sub"
}
这种模式严格来说不算是“继承”,因为这种模式不符合原型链的概念,他只是使用call来执行父对象的构造函数,强行改变this指向,在子对象中添加了父对象的属性
- 组合继承
组合继承是为了在构造函数继承的模式下优化原型链所提出的
//父对象构造函数
function SuperType(name){
this.man = name
}
//子对象构造函数
function SubType(){
SuperType.call(this,"super")
this.name = "sub"
}
SubType.prototype = new SuperType()
SubType.prototype.constructor = SubType
组合继承就是在构造函数的基础上加了最后两句来操作原型,这两句的作用是:
- SubType.prototype = new SuperType()
这一句很明显是把SuperType实例赋值给子类构造函数的prototype属性,也就是原型链操作,这个操作导致了SubType.prototype.constructor被改写成SuperType
这一点这么理解:由于SubType.prototype被改写,原始的继承关系也会被改写,而new SuperType()所带来的属性(包括父类的属性)都会覆盖SubType.prototype - SubType.prototype.constructor = SubType
这一句就是为了弥补上面的副作用,把constructor改会正常的SubType
- 寄生组合继承
function SuperType(name){
this.man = name
}
//子对象构造函数
function SubType(){
SuperType.call(this,"super")
this.name = "sub"
}
//创建一个__proto__原型为SuperType.prototype的对象
let obj = Object.create(SuperType.prototype)
//改写构造函数为子类
obj.constructor = SubType
//改写原型
SubType.prototype = obj
这中模式干了两件事 SuperType.call(this,"super") 混合对象,let obj = Object.create(SuperType.prototype) 继承对象,原理理解了其实和组合继承差不多,但是只调用了一次父类
class继承
class SuperType {
constructor(name){
this.name = name
}
}
class SubType extends SuperType {
constructor(name,age){
super(name)
this.age = age
}
}
其中super关键字为父类构造函数的代写
this
this是一个运行时绑定的值,简单来说就是谁调用函数,函数内的this绑定谁
而箭头函数的this是闭合语法上下文的值,也就是定义时的值,而是用bind,apply,call可以改变绑定
bind,apply,call的区别
- bind执行后返回的是一个改变了this指向的函数,而其他两种是直接执行的结果
- apply接受的第一个参数是执行上下文,第二个参数是一个参数数组,其他两种第一个参数是执行上下文,被调用函数参数顺序传入
- 手写call
Function.prototype.myCall = function(context=window,...rest){
//这里的this就是需要改变对象的函数
context.fn = this;
const result = context.fn(...rest);
delete context.fn
return result;
}
2.手写apply
Function.prototype.myApply = function(context=window,rest=[]){
if(!Array.isArray(rest)){
throw Error("必须是数组")
}
//这里的this就是需要改变对象的函数
context.fn = this;
const result = context.fn(...rest);
delete context.fn
return result;
}
2.手写bind
Function.prototype.myBind = function(context=window,...rest){
//这里的this就是需要改变对象的函数
let _this = this
let Noop = function(){};
let fToBind = function(){
return _this.apply(context,rest)
}
//支持使用new调用新创建的构造函数
// 把要被绑定的prototype拿回来
if(_this.prototype){
Noop.prototype = _this.prototype
}
fToBind.prototype = new Noop()
return fToBind
}
reflect
- reflect的作用
- 将一些属于对象内部的方法,放到reflect上,例如Object.defineProperty,现在是Reflect.defineproperty
- 改善了一些操作的返回值,例如Object.defineProperty没有找到属性时会报错,Reflect.defineproperty会返回false
- 让Object操作都变成函数行为,例如delete obj[name]可以写为 Reflect.deleteProperty(obj, name)
- Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法
Proxy
基础用法是let p = new Proxy(target,handler)
target是你要改写的对象,handler是操作options,包含很多操作钩子,get,set之类