对象创建回顾
- 对象直接量
- 使用new操作符
- Object.create() 方式创建
我们创建对象一般都是创建单个对象,如果有需求要求创建多个对象,这就可能出现很多重复的代码。
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前端))