第二十节:面向对象设计模式

对象创建回顾

  • 对象直接量
  • 使用new操作符
  • Object.create() 方式创建

参考:第三节:数据类型——Object对象

我们创建对象一般都是创建单个对象,如果有需求要求创建多个对象,这就可能出现很多重复的代码。

this指向解析

①函数中:this表示全局作用域,如果在浏览器中就表示window

var x=5;
function fun(){
  let x=1;
  console.log(this.x,this);
}
fun();//5,window,this指向全局作用域

②对象的方法和属性:指向对象

var x=5;
var obj={
  x:1,
  f:function(){console.log(this.x)}
}
obj.f();//1,this指向obj这个对象

③构造函数中,this指向创建出来的新对象

function fun(x,y){
this.x=x;
this.y=y;
console.log(this.x,this.y,this);
}
const newF1=new fun(1,5);//结果:1 5 fun {x: 1, y: 5}

④强制指向某一对象,需要使用到函数apply()和call()
call() 和 apply() 之间的区别:
call() 方法分别接受参数。
apply() 方法接受数组形式的参数

//不接收参数
var person = {
  firstName:'leo',
  lastName:'dd',
  fullName: function() {
    return 'Hello ' + this.firstName + " " + this.lastName + '!'
  }
}
var person1 = {
  firstName:"John",
  lastName: "Doe"
}
person.fullName();//"Hello leo dd!"
person.fullName.apply(person1)//"Hello John Doe!"
person.fullName.call(person1);//"Hello John Doe!"
//接收参数
var person = {
  firstName:'leo',
  lastName:'dd',
  fullName: function(city, country) {
    return this.firstName + " " + this.lastName + "," + city + "," + country;
  }
}
var person1 = {
  firstName:"John",
  lastName: "Doe"
}
person.fullName('USA','count');//"leo dd,USA,count",默认this指向为person对象
person.fullName.apply(person1,['USA','count']);//"John Doe,USA,count",将this指向修改为person对象
person.fullName.call(person1,'USA','count');//"John Doe,USA,count",将this指向修改为person对象

参考:特殊对象-函数详解
参考:JavaScript的this原理——阮一峰

创建对象过程中使用的模式及优缺点

1、工厂模式:把创建对象的过程使用函数包裹起来,每次创建对象运行函数即可。相当于此函数为创建对象的工厂。

function createPerson(name,age,job){
  var obj={};
  obj.name = name;
  obj.age=age;
  obj.job=job;
  return obj;
}
var zs=createPerson('张三',18,'学生');
var ls=createPerson('李四',27,'设计师');
zs.constructor;//ƒ Object() { [native code] },是无法判断是否由createPerson创建的对象

缺点:无法识别对象的类型

2、构造函数模式
构造函数一般约定俗成第一个字母大写。
constructor 属性:就是一个指针,指向了当前对象的构造函数。可判断两个对象是否是同一个构造函数所创建的。

new操作符执行的过程:

  • 创建一个对象
  • 将构造函数作用域赋值给新的对象
  • 实现函数里面所有代码
  • 将对象返回回来
function Person(name,age,job){
	this.name = name;
	this.age = age;
	this.job=job;
	this.cName=function(){
		alert(this.cName)
	}
}
var per1=new Person('张三',18,'学生');
var per2=new Person('李四');
per1.constructor == Person;//true 判断per1是否为Person此构造函数构造而成
per2.constructor == per1.constructor;//true  指向同一个构造函数
per2.constructor === per1.constructor;//true

基础数据类型的检测可使用typeof运算符,typeof查询对象返回的均为Object,而构造函数的检测可使用instanceof运算符

function Person(name,age,job){
	this.name = name;
	this.age = age;
	this.job=job;
    this.cname=function(){
       alert(this.cname);
    }
}
var per1=new Person('张三',18,'学生');
var obj={name:'测试'};
typeof per1;//'object'
per1 instanceof Object;//true
per1 instanceof Person; //true
obj instanceof Person; //false
obj instanceof(Person);//false
//工厂模式创建的函数constructor就是Object,而构造函数返回的为构造函数的名称
zs.constructor;//ƒ Object() { [native code] }
per1.constructor;//ƒ Person(name,age,job){ this.name = name; this.age = age; this.job=job;}

构造函数与普通函数没有区别,只是使用了new操作符来创建它。this指向了新的对象。
构造函数缺点:
每一个实例化出来的对象,都有一个cname的方法,虽然功能相同,但是per1.cname == per2.cname;为false,所以会创建出来很多功能相同的方法,占用内存。解决方法看下面代码:

//先创建一个公共方法,然后将此公共方法赋值给构造函数的一个属性;
var cc=function(){
	alert(this.cname);
}
function Person(name,age,job){
	this.name = name;
	this.age = age;
	this.job=job;
    this.cname=cc;
}
var per1=new Person('张三',18,'学生');
var per2=new Person('李四',25,'设计');
per2.cname == per1.cname;//true

此方法的问题公共方法占内存且无法避免重名问题,所以出现另一个模式,原型模式
3、原型模式
在JavaScript中,prototype对象是实现面向对象的一个重要机制。
原型对象:对于任何一个 函数 都有一个 prototype属性,prototype对应的是一个对象,这个对象就被称之为原型对象。(每一个对象都有一个constructor 属性)

function person(){}
person.prototype;//{constructor: ƒ}
//在原型对象上增加属性
person.prototype.name='测试';
person.prototype.age=18;
person.prototype;//{age: 18, name: '测试', constructor: ƒ}
//实例化对象,会得到原型对象上的所有属性和方法,解决了多个对象拥有同一个方法和属性时,每次创建都生成一个新的属性和方法占用内存
var per1=new person();
var per2=new person();
per1.name;//'测试'
per1.name==per2.name;//true

为什么原型模式上per1.name==per2.name;
每一个对象的内部,都有一个指针,指向了它的构造函数的原型对象,正常情况下这个指针是访问不到的,但是现在大部分浏览器都可通过__proto__访问到,__proto__指向person的原型对象。

per1.__proto__;//获取per1的构造函数的原型对象 {age: 18, name: '测试', constructor: ƒ}

查找属性:
当使用per1.name;访问name属性时,会先在person上面查找有没有此属性,有的话返回回来,如果没有则会顺着prototype属性找原型对象上面的方法,如果有则返回,没有则返回undefined;这就是原型链查找

per1;//person {}
person.prototype;//{age: 18, name: '测试', constructor: ƒ}
per1.job='工作';
per1;//person {job: '工作'}

创建/设置属性:
创建属性时不会查找prototype原型链,会判断对象上有没有此属性,没有则直接创建。此时会出现一个问题(同名屏蔽问题)。
解决方法:一是尽量避免同名;如果同名可以使用delete将同名属性删除。

per1.job='工作';
per1;//person {job: '工作'}
per1.name='修改名称';
per1;//person {job: '工作',name: '修改名称'}
per1.name;//修改名称,会直接调取person中的那么属性,不回查找prototype属性的原型对象
delete per1.name;
per1.name; //'测试'

对象的属性和方法均可使用delete进行删除。
为什么所有对象都可使用toString和valueOf方法:这是因为这两个方法是存在在原型对上的方法。所以在对象上找不到时,好到根节点原型对象时,也是可以找到这两个方法的。
在这里插入图片描述除了toString和valueOf方法,还有isPrototypeOf(),
isPrototypeOf():返回一个布尔值,用于表示该方法所调用的对象是否在指定对象的原型链中,如果存在就返回 true,否则就返回 false
hasOwnProperty():返回一个布尔值,用于表示一个对象自身是否包含指定的属性,该方法并不会查找原型链上继承来的属性

//isPrototypeOf()用于测试一个对象是否存在于另一个对象的原型链上
var f = function () {}  //定义函数
f.prototype = {  //函数的原型对象
    a : 1,
    b : function () {
        return 2;
    }
}
var b = new f(); //实例化对象
f.prototype.isPrototypeOf(b);//true  f.prototype就是对象b的原型对象 
//判断age和a属性是否属于b对象
b.age=20;
b.hasOwnProperty('a');//false   a为原型上的属性
b.hasOwnProperty('age');//true   age为b的属性

参考:对象的实例方法
for in循环能获取所有可枚举属性,不仅能获取当前对象的属性,还可获取原型链上的属性
获取所有属性、自由属性、原型属性

Object.prototype.score = '97';
var people = {
    name:'小红',
    age : 15,   
}
//获取所有属性
for(let key in people ){
    console.log(key+':'+people[key]);
}
//获取自由属性方法一
for(let key in people ){
    if(people.hasOwnProperty(key)){
       console.log(key+':'+people[key]);
    }
}
//获取自由属性方法二
Object.keys(people);//['name','age']
//获取原型属性,所有属性去除自由属性即可获得原型属性
var arr=[];//所有属性
for(let key in people ){
    arr.push(key);
}
var narr=Object.keys(Person); //自由属性
var proArr=[]; //原型属性
for(let i=0;i<arr.length;i++){
	if(narr.indexOf(arr[i])<0){
		proArr.push(arr[i]);
	}
}
proArr; //['score']

备注:本内容学习总结来源于喜马拉雅冰山工作室沙翼老师主讲的(陪你读书(JavaScript WEB前端))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值