JS面试内容之对象 |
您所在的位置:网站首页 › 类的定义必须包含在哪种符号 › JS面试内容之对象 |
对象的创建方式
三种基本方式
Object构造函数
let person1 = new Object();
person1.name = "xxy";
person1.sayName = function() {
console.log("My name is " + this.name);
}
复制代码
对象字面量
let person2 = {
name: "xxy",
sayName: function() {
console.log(this.name);
}
};
复制代码
Object.create,该方法创建一个新对象,使现有对象作为新对象的原型
let person3 = Object.create(person1);
复制代码
类(语法糖)方式
class Person{
constructor(name) {
this.name = name;
}
sayName() {
console.log(this.name);
}
}
let person4 = new Person("xxy");
复制代码
四种模式方式
工厂模式,抽象了创建特定对象的过程
function createPersonFactory(name) {
let person = new Object();
person.name = name;
person.sayName = function() {
console.log(this.name);
};
return person;
}
复制代码
该方式有一个缺点,就是通过工厂创建出来的对象没有标识,即不知道是什么类型,即使由不同的工厂创建出不同的对象,标识都是Object,没有区分度。 构造函数模式 function Person(name) { this.name = name; this.sayName = function() { console.log(this.name); } } let p = new Person("xxy"); 复制代码创建方式和工厂模式类似,只不过构造模式不需要new Object,这是因为在外部使用该函数去new的时候,会在内存中创建一个对象,将其赋值给构造函数内部的this。 也不需要自己手动返回对象,这是因为构造函数没有返回对象的话,默认返回this指向的对象。 最重要的一点是,创建出来的对象有了标识,是Person的实例对象,这是因为在内存中创建的哪个对象,该对象的[[prototype]]的构造函数指向了构造函数的prototype 虽然方便了很多,但是也有一个缺点,是以上两种方式都存在的,即方法体重复,同一个构造函数创建出来的对象,方法应该是共有的,而不是每个实例独一份的。 原型模式,在原型上定义方法和属性,可以被每个实例使用,是共享的 function Person() {} Person.prototype.sayName = function() { console.log(this.name); }; let p1 = new Person("xxy"); let p2 = new Person("xxxy"); 复制代码 组合模式,即构造模式和原型模式的组合 function Person(name) { this.name = name; } Person.prototype.sayName = function() { console.log(this.name); }; 复制代码 如何判断一个对象属于哪个类判断数据类型有四种判别方式 typeof,用于判断数据的基本类型,判别引用类型不是很准确。至于null为什么也是object,这是一个设计缺陷。 instanceof,用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上 Object.prototype.constructor() Object.prototype.toString(),可以判断内定的数据类型,用户自定义的数据类型仍然显示为Object 补充 模拟new实现 在内存中创建一个对象 新对象的[[Prototype]]赋值为构造函数的prototype 构造函数内部的this赋值为新对象 执行构造函数内部代码 如果构造函数返回非空对象,则返回该对象,否则返回新对象 function newOperator(Constructor,...args) { let thisValue = Object.create(Constructor.prototype); // 对应1、2 let result = Constructor.apply(thisValu,args); // 对应3、4 return typeof result === 'object' && result !== null ? result : thisValue; // 对应5 } 复制代码 模拟Instanceof实现 不断寻找原型链,直到找到某个构造函数的原型或者原型链指向null function instanceOf(obj,cons) { let protoCons = cons.prototype; // 获取构造函数的原型 let protoObj = Object.getPrototypeOf(obj); // 获取对象的原型 while(protoObj !== null) { if(protoCons === protoObj) { return true; } protoObj = Object.getPrototypeOf(protoObj); } return false; } 复制代码 利用Object.prototype.toString.call(...)+typeof来自定义判别符号 function myTypeOf(value) { if(typeof value !== 'object' || typeof value === 'function') { return (typeof value).toLowerCase(); } let result = Object.prototype.toString.call(value); return result.slice("[Object ".length,result.length - 1).toLowerCase(); } 复制代码 继承和原型链原型链是主要的继承方式,即通过原型链继承多个类型的属性和方法。每个构造函数都有一个原型对象,原型有一个属性指回构造函数,而实例有一个内部指针指向原型。原型对象也是实例,那么其内部也有一个指针指向另一个原型。可以通过该特性,让每个构造函数的原型串起来,共享其方法和属性,从而实现继承。 原型 默认情况下,所有的引用类型都继承Object 原型和继承关系有两种方法可以判别实例属于什么类型 instanceof操作符 isPrototypeOf()方法 获取原型链__proto__是每个原型对象的可访问属性,是内部原型链[[Prototype]]对象对外暴露的可访问属性。但不是所有浏览器都实现了使用__proto__去访问,为了确保兼容性,可以调用Object.getPrototypeOf()获取。 继承方式在JS中,有以下五种继承方式 原型继承,如果原型是另一个类型的实例 function Parent() { this.property = ["house","car"]; } function Child() {} Child.prototype = new Parent(); let parent = new Parent(); parent.property.push("money"); let child1 = new Child(); child1.property.push("toy"); let child2 = new Child(); 复制代码原型继承有两个缺点,第一个是在原型上的引用属性共享,一个实例修改会在另一个实例上也能看见,第二个就是子类无法向父类传递参数。 借用构造函数继承 function Parent(name) { this.property = ["house","car"]; this.name = name; } function Child(name) { Parent.call(this,name); } let child1 = new Child("xxy"); let child2 = new Child("xxxy"); child1.property.push("toy"); 复制代码不再通过原型去调用属性,而是直接调用父类的构造函数,让每个实例独一份属性。解决了原型继承的两个缺点。但是有一个缺点就是,如果父类有方法,那么创建实例对象都会创建一遍方法。 组合继承,即组合原型和构造函数 function Parent(name) { this.property = ["house","car"]; this.name = name; } Parent.prototype.sayName = function() { console.log(this.name); } function Child(name) { Parent.call(this,name); // 子类创建的时候调用父类构造函数,2次 } Child.prototype = new Parent(); // 1一次 Child.prototype.counstructor = Child; let child1 = new Child("xxy"); let child2 = new Child("xxxy"); child1.property.push("toy"); 复制代码效率不好,调用了两次父类的构造函数 寄生式继承,创建一个仅仅用于封装继承过程的函数 function createObj(obj) { let res = Object.create(obj); res.say = function() { consoloe.log("hi"); } return res; } 复制代码缺点和借用构造函数一样。 寄生组合继承 function Parent(name) { this.property = ["house","car"]; this.name = name; } Parent.prototype.sayName = function() { console.log(this.name); } function Child(name) { Parent.call(this,name); } function createObj(obj) { function F() {} F.prototype = obj; return new F(); } function prototype(child,parent) { let proto = createObj(parent.prototype); proto.constructor = child; child.prototype = proto; } prototype(Child,Parent); 复制代码只调用了一次父类的构造方法,且避免了子类原型上存在不必要的的属性。 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |