JS知识点梳理

JS数据类型

【基本数据类型】、【引用数据类型】

区别:
【基本数据类型】:直接存储在栈(stack)中,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储。

【引用数据类型】:同时存储在栈(stack)和堆(heap)中,占据空间大、大小不固定。引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。

包括:
【基本数据类型】:String、Number、Boolean、Null、Undefined【Symbol(es6新增,表示独一无二的值)、BigInt(es10新增)】【5个常用,2个不常用,共7个】

【引用数据类型】:Object 【包括:Object ,Array,Function,Date…】【理论上就1个】

所以可以回答说JS数据类型一共8

JS数据类型的判断方法

【 typeof 】、【 instanceof 】、【 Object.prototype.toString.call() 】

typeof:
只能检测出string、number、boolean、undefined、function、object、【symbol、bigint】(6种常用,2种不常用)

注意:【 null => object 】,其他引用数据类型,除了【 function 】,其他都是输出【 object 】

console.log(typeof ('123')) // string
console.log(typeof (123)) // number
console.log(typeof (true)) // boolean
console.log(typeof (null)) // ***object***
console.log(typeof (undefined)) // undefined
console.log(typeof (Symbol())) // symbol
console.log(typeof (BigInt(9007199254740991))) // bigint

console.log(typeof ({})) // object
console.log(typeof ([])) // object
console.log(typeof (function(){})) // function
console.log(typeof (new Date())) // object

instanceof:
这玩意用来判断JS数据类型,可以说是非常鸡肋,但是面试要问

注意instanceof可以准确的判断 【引用数据类型】,但是却无法判断【基本数据类型】

console.log('123' instanceof String);                // false
console.log(123 instanceof Number);                  // false
console.log(true instanceof Boolean);                // false

console.log([] instanceof Array);                    // true
console.log(function () { } instanceof Function);    // true
console.log({} instanceof Object);                   // true
console.log(new Date() instanceof Date);             // true

Object.prototype.toString.call()
非常牛逼,什么类型都能判断出来,但平时项目中,因为太长不用…总体出场率并没有typeof高

console.log(Object.prototype.toString.call('123'))          // [object String]    
console.log(Object.prototype.toString.call(123))            // [object Number]
console.log(Object.prototype.toString.call(true))           // [object Boolean]
console.log(Object.prototype.toString.call(null))           // [object Null]
console.log(Object.prototype.toString.call(undefined))      // [object Undefined]
console.log(Object.prototype.toString.call(Symbol()))       // [object Symbol]
console.log(Object.prototype.toString.call(BigInt(1)))      // [object BigInt]

console.log(Object.prototype.toString.call({}))             // [object Object]
console.log(Object.prototype.toString.call([]))             // [object Array]
console.log(Object.prototype.toString.call(function(){}))   // [object Function]
console.log(Object.prototype.toString.call(new Date()))     // [object Date]

可以简单的封装成一个函数

function myTypeOf(value) {
  return Object.prototype.toString.call(value).slice(8, -1)
}
console.log(myTypeOf('123'))          // String    
console.log(myTypeOf(123))            // Number
console.log(myTypeOf(true))           // Boolean
console.log(myTypeOf(null))           // Null
console.log(myTypeOf(undefined))      // Undefined
console.log(myTypeOf(Symbol()))       // Symbol
console.log(myTypeOf(BigInt(1)))      // BigInt

console.log(myTypeOf({}))             // Object
console.log(myTypeOf([]))             // Array
console.log(myTypeOf(function () { }))   // Function
console.log(myTypeOf(new Date()))     // Date

JS数据类型的相互转换

JS类型转换只有三种情况:
【1.】 转换为布尔值(调用Boolean()方法)
【2.】 转换为数字(调用Number()parseInt()parseFloat()方法)
【3.】 转换为字符串(调用.toString()或者String()方法)

注意!!!
null、underfined 没有 .toString 方法

String(null)      // null
String(undefined) // undefined

<-- 常见的 布尔类型 转换 -->
<-- 只有这6个值转化为boolean时为false,其他的都是true{}[]也是true -->
+0(0)-0""NANnullundefined

<-- 常见的 数字 转换 -->
Number(null)       // 0
parseInt(null)     // NAN
parseFloat(null)   // NAN

Number({})		   // NAN
Number([])		   // 0
Number([123])	   // 123
Number([123,456])  // NAN

<-- 常见的 字符串 转换 -->
['刻晴', 101, true, null, undefined].toString()  // 刻晴,101,true,, null和undefined无法转化,所以为空
{}.toString() // [object Object]

undefined 与 undeclared 的区别

已在作用域中声明但还没有赋值的变量,是 undefined。相反,还没有在作用域中声明过的变量,是 undeclared 的


null 和 undefined 的区别?

undefined:表示未定义,一般变量声明了但还没有定义的时候会返回 undefined
null:表示空,无,就是啥也没有

其实 null 不是对象,虽然 typeof null 会输出 object,但是这只是 JS 存在的一个悠久 Bug。
在 JS 的最初版本中使用的是 32 位系统,为了性能考虑使用低位存储变量的类型信息,
000 开头代表是对象,然而 null 表示为全零,所以将它错误的判断为 object 。
虽然现在的内部类型判断代码已经改变了,但是对于这个 Bug 却是一直流传下来。

this的理解

this的四种绑定规则

  1. 默认绑定
  2. 隐式绑定
  3. 显示绑定
  4. new绑定

【1 .】默认绑定

全局this默认指向window(其实是指向全局对象,但是这里默认是浏览器,浏览器的全局对象是window,如果是在node环境中,或者严格模式下,就不是指向window了)

console.log(this === window) // true

函数独立调用中的this,默认也是指向window的,立即执行函数同理(无论这个立即执行函数在什么地方,函数中的this都指向window
【函数独立调用:意思是调用该函数时,直接使用testThis()的形式进行调用,或者是window.testThis()

普通的函数独立调用

function testThis() {
	console.log(this === window); // true
}
立即执行函数(无论在哪里,都指向window,除非主动使用call,apply等更改)

function testThis() {
	console.log(this === window); // true
	(function () {
      console.log(this === window); // true
    })()
}
testThis();

let obj = {
  a: '123',
  foo: (function () {
    console.log(this === window);
  })(),
};

【2 .】隐式绑定【存在(隐式丢失,参数赋值)】
通过【对象】【属性】的方式调用该方法时,该方法中的this,指向该调用者,
也就是我们常说的谁调用,就指向谁(函数的this指向大概率跟执行方式有关)

【this是在函数执行时,产生的一个AO/GO(AO函数执行的上下文,GO是一个global),在AO中都会有相应的this指向的问题,也就是说,只有函数执行this指向才有意义,函数不执行,this就没有意义
预编译时,这个this指向就已经确定下来了,函数在执行时才会产生自身的this执行
意思就是,每一个this执行都是函数执行时,才会产生的】

<!--这是一个最简单的【隐式绑定】,此时this指向obj本身,也就是谁调用了,就指向谁 -->
let obj = {
  a: 'zhangsan',
  foo: function () {
    console.log(this);
  },
};
obj.foo()
// 一个稍微复杂点的【隐式绑定】
var obj = {
  a: 2,
  foo: function () {
    console.log(this); // 指向obj
    function testThis() {
      console.log(this); // 指向window
    }
    testThis(); // 被认定为函数的独立调用

    (function () {
      console.log(this); // 指向window
    })();

    // 当函数执行时,导致函数被定义,并抛出(闭包,闭包是一种Js现象,不是语法规则)
    function closure() {
      console.log(this); // 指向window
    }
    return closure;
  },
};
obj.foo(); // obj调用其中的属性,而这个属性是一个函数,属于,谁调用指向谁
obj.foo()(); // 此时函数中有闭包,整个函数执行完,返回一个函数,也就是这里的obj.foo()();其实就是closure()属于独立调用

隐式调用的特殊情况【隐式丢失,参数赋值】

function foo() {
  console.log(this);
}
var obj = {
  a: 2,
  foo: foo,
};
var bar = obj.foo; // 将对象的属性值赋值给bar,对象的属性值又指向foo函数,这里只是简单的赋值,并没有函数执行
bar() // 所以这里执行时,就等于foo(),所以指向window
var a = 0;
function foo() {
  console.log(this);
}
function bar(fn) {
  fn();
}
var obj = {
  a: 2,
  foo: foo,
};
// 参数由实参变为形参的过程,是一个【浅拷贝】的过程
bar(obj.foo); // 指向window

【3 .】显示绑定【call,apply,bind(区别在于传递参数的不同)】
两类参数,第一个是要改变为this指向的对象第二个是要传递的参数
当第一个参数不是对象时,如('123',1,true),会指向他们的包装类String {"123"}Number {1}Boolean {true}
但是null和undefined是没有包装类的,此时会指向默认的window

// 一个最简单的【显示绑定】
function testThis() {
  console.log(this);
}
let obj = {
  a: '123',
};
testThis.call(obj);
function foo(a, b, c) {
  console.log(this);
}
var obj = {
  a: 2,
  foo: foo,
};
var bar = obj.foo; // 将对象的属性值赋值给bar,对象的属性值又指向foo函数,这里只是简单的赋值,并没有函数执行
obj.foo(1, 2, 3);          // 隐式绑定,指向 obj {a: 2, foo: ƒ}
bar.call('123', 1, 2, 3);  // 显示绑定,指向包装类String {"123"}
bar.call(1, 1, 2, 3);      // 显示绑定,指向包装类Number {1}
bar.call(true, 1, 2, 3);   // 显示绑定,指向包装类Boolean {true}
bar.apply(obj, [1, 2, 3]); // 显示绑定,参数不同 指向 obj {a: 2, foo: ƒ}
bar.bind(obj)(1, 2, 3);    // 显示绑定,参数不同 指向 obj {a: 2, foo: ƒ}

【4 .】new绑定

new一个对象的过程(对象实例化的过程)

1. 创建一个空对象
2. 将this指向这个空对象{}
3. 执行构造函数中的代码,也就是对this进行赋值
4. 将这个this返回回去(这个this就是函数实例化执行的结果,也就是说这个this就是实例化之后的对象,因为将这个this赋值给这个对象了嘛)
// 一个简单的,构造函数+实例化过程
function Person(name, age, sex) {
  this.name = name;
  this.age = age;
  this.sex = sex;
}
var person = new Person('zhangsan', '24', 'male');
实例化过程中最重要的就是,构造函数的return值(但是一般我们也不会手动指定返回值,因为构造函数本身会隐式的返回this)
1. 这里手动添加的返回值如果是{},[],function,则会改变this的指向
2. 如果是其他值,那么任然会指向实例化后的对象本身
function Person(name, age, sex) {
  this.a = 1;
  return {
    name: 2,
  };
}
var person = new Person('zhangsan', '24', 'male');
console.log(person); // {name: 2}
function Person(name, age, sex) {
  this.a= 1;
  return 123;
}
var person = new Person('zhangsan', '24', 'male');
console.log(person) // Person {a: 1}

this的绑定优先级

new绑定 > 显示绑定 > 隐式绑定 > 默认绑定

练习

// 那么我们可以做一道题检测一下
var number = 5;
var obj = {
  number: 3,
  fn1: (function () {
    var number;
    this.number *= 2;
    number = number * 2;
    number = 3;
    return function () {
      var num = this.number;
      this.number *= 2;
      console.log(num);
      number *= 3;
      console.log(number);
    };
  })(),
};
var fn1 = obj.fn1;
fn1.call(null);
obj.fn1();

解析

var number = 5;
var obj = {
  number: 3,
  fn1: (function () {
    var number; // 定义一个number
    this.number *= 2; // 因为是自执行函数,所以指向window,所以this.number = this.number*2值得是5*2 = 10
    number = number * 2; // number只被定义,没被赋值,所以是undefined,undefined*2=NAN
    number = 3; // 但是这一步number又被重新赋值,所以number = 3
    return function () {
      var num = this.number;
      this.number *= 2;
      console.log(num);
      number *= 3;
      console.log(number);
    };
  })(),
};
// 这里将对象的一个属性,赋值给fn1,但是这是个自执行函数,在赋值后直接就可以执行了,此时,因为他是自执行函数(注意,立即执行函数作为属性的值时,在对象定义完成后就会直接执行,但此时里面是个闭包函数,所以没有任何值输出)
// 自执行函数无论放在哪里都执行window,所以这一步,fn1中的this执行window
var fn1 = obj.fn1;
// 这里使用【显示绑定】,将闭包函数指向null,上面提到,null是没有包装类的,所以会指向默认的window
// 这里是纯执行闭包中的代码
// function () {
//   var num = this.number;     num = 10,因为外层函数已经将window中的number变成10了
//   this.number *= 2;          this.number = 20,这里又把window中的number变成20
//   console.log(num);			输出 10
//   number *= 3;               number是外层函数定义的值,是3,这里3*3 = 9
//   console.log(number);		所以这里输出9
// };
fn1.call(null); // 10 9
// 这里是典型的【隐式绑定】所以这里的this执行的就是obj这个对象本身
// 这里是纯执行闭包中的代码
// function () {
//   var num = this.number; 	this指向obj本身,num = 3
//   this.number *= 2;          this.number = this.number * 2 = 6这里又把obj中的number变成6
//   console.log(num);			num = 3,所以输出3
//   number *= 3;               因为这个闭包的原因,外层调用的number并没有被释放,所以number = 9 * 3 = 27
//   console.log(number);		这里输出27
// };
obj.fn1();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值