【JS】数据类型、原型和继承

271 阅读4分钟

参考

2020年前端面试复习必读精选文章

数据类型

值的类型

变量没有类型,值才有类型。值有 7 种基本类型和 1 种引用类型:

  • Null:null
  • Undefined:undefined
  • Boolean:truefalse
  • Number:如42-36
  • BigInt:如9007199254740992n
  • String:如'hello world'
  • Symbol:如Symbol('sign')
  • Object:如{}[]function() {}等。

类型转换

在 JS 中,类型转换均为强制类型转换,其分为:

  • 显式强制类型转换
  • 隐式强制类型转换
// 强制类型转换
var a = 42;
var b = String(a); // 显式强制类型转换
var c = a + "hello"; // 隐式强制类型转换

// 等于与全等的区别
a == b; // true,允许比较时进行强制类型转换
a === b; // false,不允许比较时进行强制类型转换

// 输出时自动调用变量的toString方法
console.log(a, b, c);

类型判断

使用typeof操作符获取值类型的字符串值:

typeof undefined; // 'undefined'
typeof true; // 'boolean'
typeof 42; // 'number'
typeof 9007199254740992n; // "bigint"
typeof "42"; // 'string'
typeof Symbol(); // 'symbol'
typeof {}; // 'object'
typeof []; // 'object'
typeof new Map(); // 'object'
typeof new Set(); // 'object'

// null和函数例外
typeof null; // 'object'
typeof function () {}; // 'function'

// 未声明和未赋值的变量都返回undefined
var a;
typeof a; // 'undefined'
typeof b; // 'undefined',实际上应为undeclared

使用toString函数获取值的内部类型:

function toString(obj) {
  return Object.prototype.toString.call(obj);
}

toString(null); // '[object Null]'
toString(undefined); // '[object Undefined]'
toString(true); // '[object Boolean]'
toString(42); // '[object Number]'
toString(9007199254740992n); // '[object BigInt]'
toString("42"); // '[object String]'
toString(Symbol()); // '[object Symbol]'
toString({}); // '[object Object]'
toString([]); // '[object Array]'
toString(new Map()); // '[object Map]'
toString(new Set()); // '[object Set]'
toString(new Date()); // '[object Date]'
toString(/regex-literal/i); // '[object RegExp]'
toString(function () {}); // '[object Function]'

使用instanceof操作符检测某个实例对象的原型链上是否有指定构造函数的原型对象:

function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}
const auto = new Car("Honda", "Accord", 1998);
console.log(auto instanceof Car); // true
console.log(auto instanceof Object); // true

原型

构造函数

当使用new调用某个函数以创建实例时,该函数可被称为构造函数。构造函数在结构上与普通函数无异:

// bar和foo都是函数
function bar() {}
function Foo() {}

var a = new bar(); // 通过new调用bar,所以bar可被称作构造函数
var b = new Foo(); // 通过new调用Foo,所以Foo可被称作构造函数

如果打算将一个函数作为构造函数使用,建议使用大写开头的命名方式。

原型对象

所有函数默认有一个prototype属性,其指向一个被称为原型对象的对象,该对象上的constructor属性指向回该函数。具体见下:

function Foo() {}
var foo = new Foo();
Object.getPrototype(foo) === Foo.prototype; // true
Foo.prototype.constructor === Foo; // true

通过构造函数创建的实例,其__proto__属性指向原型对象。原理如下:

// new的实际过程
function myNew(func, ...args) {
  var obj = new Object();
  func.apply(obj, args);
  obj.setPrototypeOf(func.prototype);
  return obj;
}

实例能访问且共享原型对象上的属性和方法,所以通常将公有方法定义在原型对象上,将私有属性定义在构造函数内。

原型链

假如一个原型对象是另一个构造函数的实例,那么该原型对象将“享有”该构造函数原型对象上的属性和方法,如此层层递进,就形成了一条关于实例与原型对象的链条,这条链条则被称为原型链

继承

创建实例

在学习继承前,最好了解以下几种创建实例的方法:

// 1.工厂模式:因为实例都是从Object实例化而来,所以无法识别实例类型
function createPerson(name, age) {
  var person = new Object();
  person.name = name;
  person.age = age;
  person.say = function () {};
  return person;
}
var tom = createPerson("Tom", 24);

// 2.原型模式:name、age等属性被共享,无法私有化
function Person() {}
Person.prototype.name = "";
Person.prototype.age = 0;
Person.prototype.say = function () {};
var tom = new Person();
tom.name = "Tom";
tom.age = 24;

// 3.构造函数模式:say等方法无法复用,造成资源浪费
function Person(name, age) {
  this.name = name;
  this.age = age;
  this.say = function () {};
}
var tom = new Person("Tom", 24);

// 4.组合模式:兼有模式3、4的优点
function Person(name, age) {
  this.name = name;
  this.age = age;
}
Person.prototype.say = function () {};
var tom = new Person("Tom", 24);

继承方法

// 父类
function Animal(name) {
  this.name = name;
}
Animal.prototype.say = function () {};

// 子类
// 1.原型链继承:Animal私有、公有属性均被克隆了一份
function Cat(name, age) {
  this.name = name;
  this.age = age;
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
Cat.prototype.mew = function () {};

// 2.借用构造函数继承:Animal的公有属性未被继承
function Cat(name, age) {
  Animal.call(this, name);
  this.age = age;
}

// 3.组合继承:兼顾2、3优点,但需要调用两次Animal
function Cat(name, age) {
  Animal.call(this, name);
  this.age = age;
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
Cat.prototype.mew = function () {};

// 4.寄生组合继承:只集成Animal公有方法和私有属性,完美
function Cat() {
  Animal.call(this);
}
Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;
Cat.prototype.mew = function () {};

// 附:Object.create原理
function createObject(obj) {
  function F() {}
  F.prototype = obj;
  return new F();
}