首页 > 技术文章 > JavaScript面向对象

liudemeng 2019-09-10 10:47 原文

JavaScript中数据类型

基本类型

  • Undefined

  • Null

  • Boolean

  • Number

  • String

引用类型

  • Object

  • Array

  • Date

  • RegExp

  • Function

  • 基本包装类型

    • Boolean

    • Number

    • String

  • 单体内置对象

    • Global

    • Math

类型判断

  • typeof

  • instanceof

  • Object.prototype.toString.call()

差别

  • 基本类型在内存中占据固定大小的空间, 因此保存在栈内存中

  • 基本类型变量的赋值,复制的就是值得副本

  • 引用类型的值是对象,保存在堆内存

  • 包含引用类型值得变量实际上包含的不是对象的本身,二十一个指向对象的指针

  • 引用类型进行赋值的时候, 复制的是引用指针, 两个变量指向同一对象

小结

  • 类型检测方式

  • 基础类型和引用类型复制, 以及存储方式

  • 方法中 数据传递

JavaScript执行流程

预解析

  • 全局预解析(变量和函数提前声明, 同名的函数比变量函数优先级高)

  • 函数内部预解析(所有的变量,函数和形参都会参与预解析)

    • 函数

    • 形参

    • 普通变量

执行

  • 先解析全局作用域,然后执行全局作用域中的代码

  • 在执行全局代码中遇到的函数, 遇到函数就进行函数的预解析,在执行函数内代码

JavaScript面向对象

封装, 继承, 多态

创建对象

简单方式
var person = new Object();
person.name = "Jack";
person.age = 18;
person.sayName = function(){
   console.log(this.name);
}
var person = {
   name: "jack",
   age: 18,
   sayName: function(){
       console.log(this.name);
  }
}
改进:工厂函数
function createPerson(name, age){
   return {
       name: name,
       age: age,
       sayName: function(){
           console.log(this.name);
      }
  }
}

实例化对象

var p1 = createPerson("jack", 18);
var p2 = createPerson("luck", 20);

构造函数

function Person(name, age){
   this.name = name;
   this.age = age;
   this.sayName = function(){
       console.log(this.name);
  }
}

// 实例化对象
var p = new Person("jack", 18);
p.sayName(); // jack

解析构造函数的执行

创建Person实例, 则必须使用 new操作符

  1. 创建一个新对象

  2. 将构造函数的作用域赋值给新对象(this指的是这个新对象)

  3. 执行构造函数中的代码

  4. 返回新对象

构造函数和实例对象之间的关系

使用构造函数在于代码的简洁性,更重要的就是识别对象的具体类型。

在每一个实例对象中的_proto_中同时有一个constructor属性,该属性指向创建该实例的构造函数:

console.log(p1.constructor === Person);// true
console.log(p2.constructor === Person);//true
console.log(p1.constructor === p2.constructor);  // true

对象的constructor属性最初用来标识对象类型的, 但是检测类型还是使用instanceof操作符可靠一点

console.log(p1 instanceof Person); // true
console.log(p2 instanceof Person); // true

总结

  • 构造函数是根据具体的事物抽象出来的抽象模板

  • 实例对象是根据抽象的构造函数模板得到的具体实例对象

  • 每一个实例对象都具有一个constructor属性, 指向该实例的构造函数

    • 注意: constructor是实例的属性的说法不严谨

  • 可以通过实例的constructor属性进行判断实例和构造函数之间的关系

    • 注意: 这种方法不严谨, 推荐使用instanceof

构造函数的问题

使用构造函数方便创建对象, 但是有时候存在浪费内存

function Person(name, age) {
   this.name = name;
   this.age = age;
   this.type = "human";
   this.sayHello = function() {
       console.log("hello" + this.name);
  }
}

var p1 = new Person("jack", 18);
var p2 = new Person("luck", 18);

对于p1和p2两个对象而言,typesayHello都是一模一样的内容,造成了内存浪费

console.log(p1.sayHello === p2.sayHello); // false

对于这种问题,将共享的函数定义到构造函数外部

sayHello = function() {
    console.log('hello'+this.name)
};

function Person (name, age) {
    this.name = name;
    this.age = age;
    this.type = 'human';
    this.sayHello = sayHello;
}

var p1 = new Person('lpz', 18);
var p2 = new Person('Jack', 16);

console.log(p1.sayHello === p2.sayHello) // => true

避免全局变量命名空间冲突问题, 进行改写

var fns = {
    sayHello: function() {
      console.log('hello'+this.name);
    },
    sayAge: function() {
      console.log(this.age);
    },
};
function Person(name, age) {
    this.name = name;
    this.age = age;
    this.type = "human";
    this.sayHello = fns.sayHello;
    this.sayAge = fns.sayAge;
}

var p1 = new Person("jack", 18);
var p2 = new Person("luck", 18);

console.log(p1.sayHello === p2.sayHello); // true
console.log(p1.sayAge === p2.sayAge); //true

推荐阅读