📌 构造函数及实例化原理

new的实现原理 1、创建新对象 2、将该对象的原型与构造函数挂钩 3、通过call或apply改变构造函数this指向该新对象,并且将参数传递给该新对象 4、判断构造函数是否有返回对象或函数,无则返回创建的该对象

  1. //模拟 new 关键字实现功能
  2. function _new(/* 构造函数 */constructFunction, /* 构造函数参数 */...args) {
  3. //步骤1: 创建一个新的空对象
  4. let obj = {};
  5. //步骤2: 将该对象的 __proto__ 与构造函数constructFunction的 prototype 进行挂钩
  6. obj.__proto__ = constructFunction.prototype;
  7. //步骤3: 改变构造函数constructFunction的this指向该空对象,传入参数并执行
  8. const res = constructFunction.call(obj, ...args)
  9. //步骤4: 判断构造函数是否有返回对象或函数,有则返回,没有则返回创建的这个新对象
  10. return res instanceof Object ? rel : obj
  11. }
  12. //定义一个构造函数Preson
  13. function Preson(opt) {
  14. this.name = opt.name
  15. this.age = opt.age
  16. }
  17. //给构造函数Preson的原型prototype添加sayName方法
  18. Preson.prototype.sayName = function () {
  19. console.log('我是:' + this.name +'\n'+'今年:' + this.age);
  20. }
  21. //定义一个对象,作为实参
  22. var myInfo = {
  23. name: '张三',
  24. age: 16
  25. }
  26. //将构造函数Preson以及构造函数的实参myInfo作为_new函数的实际参数传入
  27. //得到一个通过_new函数return的实例对象
  28. var _newPreson = _new(Preson, myInfo)
  29. //调用该实例对象原型链上的sayName()方法
  30. _newPreson.sayName()
  31. //结果: 我是:张三 今年:16

原理解析**:**

  • _new函数内部通过字面量方式创建新的空对象obj
  • 将这个空对象obj的原型 proto属性指向构造函数constructFunction的prototype属性(空对象继承了构造函数原型的所有属性
  • 通过call方法改变构造函数constructFunction的this指向到这个新的空对象objobj就可以访问到构造函数中的属性
  • 判断构造函数constructFunction是否有返回的对象或函数没有返回这个空对象obj

    一句话概括:**创建新对象,修改原型链指向构造函数和把构造函数this指向新对象并返回**

**

为何不能给**构造函数**显式(手动)添加return返回值? 构造函数中 return 基本类型生成的实例都会返回一个对象

  1. //直接 return -----------------------------------------------
  2. function A(){
  3. this.a = 1
  4. return;
  5. }
  6. var a = new A() // A {}
  7. //返回 数字类型 -----------------------------------------------
  8. function B(){
  9. return 123;
  10. }
  11. var b = new B() // B {}
  12. //返回 string类型 -----------------------------------------------
  13. function C(){
  14. this.a = 3
  15. return "abcdef";
  16. }
  17. var c = new C() // C {}
  18. //返回 数组 -----------------------------------------------
  19. function D(){
  20. return ["aaa", "bbb"];
  21. }
  22. var d = new D() // ["aaa", "bbb"]
  23. //返回 对象 -----------------------------------------------
  24. function E(){
  25. this.a = 5
  26. return {a: 2};
  27. }
  28. var e = new E() // Object {a: 2}
  29. //返回 包装类型 -----------------------------------------------
  30. function F(){
  31. this.a = 6
  32. return new Number(123);
  33. }
  34. var f = new F() // Number {[[PrimitiveValue]]: 123}

一句话概括:** 使用new关键字只能返回一个对象,要么是实例对象,要么是return语句指定的对象 **

  • 如果显式return基础类型(null, undefined, Boolean, String, Number, Symbol ),对构造函数无影响,依然返回新创建实例对象
  • 如果显式return引用类型(Object, Array, function),实例对象就会返回该引用类型值直接切断了和构造函数的联系


📌 包装类

JavaScript数据类型

  • 基本类型:Undefined, Null, Boolean, Number, String
  • 引用类型:Object, Array, Date, RegExp (其实就是对象)

原始值(基本类型)不能添加属性和方法,只有对象才有**属性和方法 当给原始值添加属性,系统会自动进行包装类,其只存在于一行代码执行的瞬间**(隐式中间环节即包装类过程)


包装类处理步骤:

  • 通过new创建一个基本数据类型的实例对象
  • 调用该实例对象指定的方法
  • 销毁这个实例
    ```javascript var str = ‘Hello javascript!’; str.nickName = ‘JS’; console.log(str.nickName); //undefined

JS隐式的包装类步骤:—————————————— var str = ‘Hello javascript!’; //步骤一:隐式将原始类型字符串转为包装实例对象 new String(str).nickName = ‘JS’; //步骤二:自动创建的基本包装类型的对象只存在于一行代码执行的瞬间,后被立即销毁delete

//执行打印前new String(str).nickName = ‘JS’已被销毁 console.log(str.nickName); //undefined

  1. **
  2. > **字符串[****string]原始值****拥有length属性的原因**
  3. ```javascript
  4. var str = 'abcdefghigklmn'
  5. //str.length
  6. //JS隐式的包装类步骤:----------------------------
  7. new String(str).length
  8. 将字符串隐式经过包装为实例对象,这个实例对象中拥有内置的length属性
  9. new String(str)包装后的数据是一个伪数组(),拥有length属性
  • 【如何判断是不是伪数组】

    • 首先他是个对象
    • 是对象,有length属性
    • 有length,值必须是number类型
    • length值是number类型,并且值不为0,这个对象还得按照下标存储数据
  • 【如何判断是不是真数组】

    • 数据 instanceof Array
    • Object.prototype.toString.call( 数据 ) === ‘[object Array]’
  • 【伪数组转为真数组】

    • 方法一:Array.prototype.slice.call( 数据 )
    • 方法二:声明一个空数组,通过遍历伪数组把它们重新添加到新的数组中

基本包装类型(包装对象)

  • 为方便操作基本类型值,ECMAScript提供了三种特殊的引用类型
    • String()可以将基本数据类型String转换为String对象
    • Number()可以将基本数据Number类型转换为Number对象
    • Boolean()可以将基本数据类Boolean型转换为Boolean对象
  • 基本数据类型 undefined 和 null 不能有属性和方法

基本包装类型与引用类型的区别? 主要区别:对象的生存期

  • 引用类型**:new关键字创建的引用类型的实例,执行流离开当前作用域之前一直保存在内存中**
  • 基本包装类型**:只存在一行代码的执行瞬间,然后立即销毁**(不能再运行时添加属性及方法的根本原因)