JS面试内容之对象

您所在的位置:网站首页 类的定义必须包含在哪种符号 JS面试内容之对象

JS面试内容之对象

2023-05-14 19:40| 来源: 网络整理| 查看: 265

对象的创建方式 三种基本方式 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); 复制代码

image.png

类(语法糖)方式 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; } 复制代码

image.png

该方式有一个缺点,就是通过工厂创建出来的对象没有标识,即不知道是什么类型,即使由不同的工厂创建出不同的对象,标识都是Object,没有区分度。

构造函数模式 function Person(name) { this.name = name; this.sayName = function() { console.log(this.name); } } let p = new Person("xxy"); 复制代码

image.png

创建方式和工厂模式类似,只不过构造模式不需要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"); 复制代码

image.png

组合模式,即构造模式和原型模式的组合 function Person(name) { this.name = name; } Person.prototype.sayName = function() { console.log(this.name); }; 复制代码 如何判断一个对象属于哪个类

判断数据类型有四种判别方式

typeof,用于判断数据的基本类型,判别引用类型不是很准确。

image.png 至于null为什么也是object,这是一个设计缺陷。

instanceof,用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上

image.png

Object.prototype.constructor()

image.png

Object.prototype.toString(),可以判断内定的数据类型,用户自定义的数据类型仍然显示为Object

image.png

补充 模拟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(); } 复制代码

image.png

继承和原型链

原型链是主要的继承方式,即通过原型链继承多个类型的属性和方法。每个构造函数都有一个原型对象,原型有一个属性指回构造函数,而实例有一个内部指针指向原型。原型对象也是实例,那么其内部也有一个指针指向另一个原型。可以通过该特性,让每个构造函数的原型串起来,共享其方法和属性,从而实现继承。

image.png

image.png

原型 默认情况下,所有的引用类型都继承Object 原型和继承关系

有两种方法可以判别实例属于什么类型

instanceof操作符

image.png

isPrototypeOf()方法

image.png

获取原型链

__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(); 复制代码

image.png

原型继承有两个缺点,第一个是在原型上的引用属性共享,一个实例修改会在另一个实例上也能看见,第二个就是子类无法向父类传递参数。

借用构造函数继承 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"); 复制代码

image.png

不再通过原型去调用属性,而是直接调用父类的构造函数,让每个实例独一份属性。解决了原型继承的两个缺点。但是有一个缺点就是,如果父类有方法,那么创建实例对象都会创建一遍方法。

组合继承,即组合原型和构造函数 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