【JavaScript】整理

一、变量类型和计算


  1. JS中使用typeof能得到的哪些类型
  2. 何时使用===何时使用==
  3. JS中有哪些内置函数
  4. JS变量按照存储方式区分为哪些类型,并描述其特点
  5. 如何理解JSON

变量类型

JS中有7种内置类型,7种内置类型又分为两大类型

  • 基本类型/值类型:nullundefinedbooleannumberstringsymbol
  • 对象/引用类型:object

基本类型/值类型

是什么

把每一个值存放在对应变量内存的位置,数据分块存放在内存中,数据之间不会相互影响

var a = 100;
var b = a;
a = 200;
console.log(b); // 100

条件

原始类型存储的都是值,是没有函数可以调用的,比如undefined.toString()
在这里插入图片描述
'1'.toString() 是可以使用的。在这种情况下,'1' 已经不是原始类型了,而是被强制转换成了 String 类型(大写)也就是引用类型,所以可以调用 toString 函数

JS 的number 类型是浮点类型的,在使用中会遇到某些 Bug

  • NaN也属于number类型,并且NaN不等于自身
  • 0.1 + 0.2 !== 0.3

string类型是不可变的,无论你在string类型上调用何种方法,都不会对值有改变

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

对象/引用类型

是什么

当你创建了一个引用类型a的时候,计算机会在内存中帮我们开辟一个空间来存放值,但是我们需要找到这个空间,这个空间会拥有一个地址(指针),引用类型a存储的就是这个地址

const a = [];

对于常量 a 来说,假设内存地址(指针)为 #001,那么在地址 #001 的位置存放了值 [],常量 a 存放了地址(指针) #001

当我们将变量赋值给另外一个变量时,复制的是原本变量的地址(指针),也就是说当前变量 b 存放的地址(指针)也是 #001,当我们进行数据修改的时候,就会修改存放在地址(指针) #001 上的值,也就导致了两个变量的值都发生了改变

const a = []
const b = a
b.push(1)

产生原因

若a赋值成一个对象,特别大,a再赋值给b,b也会占很大的空间,不合理,所以引用类型是为了让内存共用空间,好几个变量共用1个内存块,节省内存空间,赋值只是变量指针的赋值,并不是每次赋值都把对象真正的值复制一份,所以值的修改相互干预

分类边界

数组array、函数function、对象object

目的

无限制扩展属性,比如说对象有个age属性,可以加第2个属性name属性

组合
深浅拷贝

条件

函数参数是对象的情况

function test(person) {
   
  person.age = 26
  person = {
   
    name: 'yyy',
    age: 30
  }
  return person;
}

const p1 = {
   
  name: 'yck',
  age: 25
}

const p2 = test(p1)
console.log(p1) // -> ?
console.log(p2) // -> ?
  • 首先,函数传参是传递对象指针的副本
  • 到函数内部修改参数的属性这步,我相信大家都知道,当前 p1 的值也被修改了
  • 但是当我们重新为 person 分配了一个对象时就出现了分歧,请看下图

在这里插入图片描述

所以最后 person 拥有了一个新的地址(指针),也就和 p1 没有任何关系了,导致了最终两个变量的值是不相同的

typeof运算符

是什么

只能区分基本类型的详细类型,引用类型无法细分

分类边界

typeof对于基本类型来说,除了null都可以显示正确的类型

typeof null // 'object' BUG
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof 1 // 'number'
typeof '1' // 'string'
typeof Symbol() // 'symbol'

typeof对于引用类型来说,除了函数都会显示object,所以说typeof并不能准确判断引用变量到底是什么类型。因为函数是一个十分特殊的引用类型,在JS中函数的地位非常高,所以需要在任何地方轻松判断出这个是函数,所以typeof单独把函数列出来

typeof [] // 'object'
typeof {
   } // 'object'
typeof console.log // 'function'

组合

instanceof

类型转换

是什么

在JS中类型转换只有三种情况

  • 转换为布尔值
  • 转换为数字
  • 转换为字符串

在这里插入图片描述
组合

转Boolean

在条件判断时,除了undefinednullfalseNaN''0-0,其他所有值都转为true,包括所有对象

引用类型转基本类型

引用类型在转换类型的时候,会调用内置的 [[ToPrimitive]] 函数,对于该函数来说,算法逻辑一般来说如下

  • 如果已经是基本类型了,那就不需要转换了
  • 调用 x.valueOf(),如果转换为基础类型,就返回转换的值
  • 调用 x.toString(),如果转换为基础类型,就返回转换的值
  • 如果都没有返回基本类型,就会报错

当然你也可以重写 Symbol.toPrimitive ,该方法在转原始类型时调用优先级最高

let a = {
   
  valueOf() {
   
    return 0
  },
  toString() {
   
    return '1'
  },
  [Symbol.toPrimitive]() {
   
    return 2
  }
}
1 + a // => 3

四则运算

目的

  • 字符串拼接
  • 运算

组合

  • 加法运算中一方为字符串,那么就会把另一方也转换为字符串
  • 加法运算中一方不是数字或字符串,那么会将它转换为数字或字符串
1 + '1' // '11'
true + true // 2
4 + [1,2,3] // "41,2,3"
  • 对于第一行代码来说,触发特点一,所以将数字 1 转换为字符串,得到结果 '11'
  • 对于第二行代码来说,触发特点二,所以将 true 转为数字 1
  • 对于第三行代码来说,触发特点二,所以将数组通过 toString 转为字符串 1,2,3,得到结果 41,2,3

条件

对于加法还需要注意这个表达式 'a' + + 'b'

'a' + + 'b' // -> "aNaN"

因为 + 'b' 等于 NaN,所以结果为 "aNaN"

Tips

  • + '1' 的形式来快速获取 number 类型
  • !!判断变量会被当做true还是false

那么对于除了加法的运算符来说,只要其中一方是数字,那么另一方就会被转为数字

4 * '3' // 12
4 * [] // 0
4 * [1, 2] // NaN

比较运算符

是什么
== > < !=

组合

如果是对象,就通过 toPrimitive 转换对象
如果是字符串,就通过 unicode 字符索引来比较

let a = {
   
  valueOf() {
   
    return 0
  },
  toString() {
   
    return '1'
  }
}
a > -1 // true

在以上代码中,因为 a 是对象,所以会通过 valueOf 转换为原始类型再比较值

题目解答

  1. JS中使用typeof能得到的哪些类型

typeof可以识别6种数据类型:number、string、boolean、object、function、undefined

  1. 何时使用===何时使用==

使用jQuery源码中推荐的写法,当obj.a为null或undefined时,看一个对象的属性是否存在或看一个函数的参数是否存在,但对象和形参必须定义,否则会报错,可简写使用==,除此外一律用===

// 看一个对象的属性是否存在
if(obj.a == null) {
   
  // 相当于obj.a === null || obj.a ===undefined,简写形式
}

// 看一个函数的参数是否存在
function(a, b) {
   
  if(a == null) {
   ...}
}
  1. JS中有哪些内置函数(数据封装类对象)

都是函数

  • Boolean
  • Number
  • String
  • Object
  • Array
  • Function
  • Date
  • RegExp
  • Error(一定要大写)

内置对象

  • Math
  • JSON
  1. JS变量按照存储方式区分为哪些类型,并描述其特点

基本类型:数据分块存放在内存中,数据不会相互干涉

var a = 100;
var b = a;
a = 200;
console.log(b); //100

引用类型:好几个变量共用1个内存块,节省内存空间,赋值只是变量指针的赋值,并不是真正值的拷贝,所以值的修改相互干预

var a = {
   age: 20};
var b = a;
b.age = 21;
conlose.log(a.age) //21
  1. 如何理解JSON
  • JSON只不过是一个JS对象而已
  • JSON也是一种数据格式
  • Math也是JS对象
JSON.stringify({
   a:10,b:20}) //将对象转换为字符串
JSON.parse('{"a": 10,"b":20}') //将字符串变为对象

二、原型与原型链


  1. 如何准确判断一个变量是数组类型
  2. 写一个原型链继承的例子
  3. 描述new一个对象的过程
  4. zepto(或其他框架)源码中如何使用原型链

构造函数

  • 构造函数首字母大写
  • 构造函数类似于模板

new一个构造函数,返回一个对象的过程

  1. new的时候把参数传入也可不传
  2. new函数执行时,创建一个空对象
  3. this指向这个新对象this = {}
  4. 执行代码,即对this.name等开始顺序赋值
  5. 赋值完后,默认return this
  6. 赋值给ff.namef.agef.class生效
function Foo(name, age){
   
  this.name = name;
  this.age = age;
  this.class = 'class-1';
  // return this    //默认有这一行
}
var f = new Foo('zhangsan', 20);
// var f1 = new Foo('lisi', 23); 可创建多个对象

Tips

  • var obj = {}其实是var obj = new Object()的语法糖
  • var arr = []其实是var arr = new Array()的语法糖
  • var fn = funtion () {...}其实是var fn = new Function()的语法糖
  • 所有的引用类型(对象、数组、函数)都有构造函数
  • 推荐使用前者的写法

原型

5条原型规则和示例

  1. 所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性(null除外)
var obj ={
   };
obj.a = 100 ;
var arr = [];
arr.a = 100;
var fn = function () {
   };
fn.a = 100;
  1. 所有的引用类型,都有一个__proto__属性(隐式原型属性),属性值是一个普通的对象
console.log(obj.__proto__);
console.log(arr.__proto__);
console.log(fn.__proto__);
  1. 所有函数,都有一个prototype属性(显式原型属性),属性值是一个普通的对象
  • Number、String、Boolean、ObjectArrayFunction、Date、RegExp、Error(一定要大写)都是函数
console.log(fn.prototype);
  1. 所有引用类型__proto__属性值指向(完全等===)他的构造函数的prototype属性值
console.log(obj.__proto__ === Object.prototype)
  1. 当试图得到一个对象的某个属性时,若果这个对象本身没有这个属性,那么在它的__proto__(即它的构造函数的prototype)中寻找
// 构造函数
function Foo(name, age){
   
  this.name = name;
}
Foo.prototype.alertName = function () {
   
  alert(this.name);
}
// 创建实例
var f = new Foo('zhangsan');
f.printName = function () {
   
  console.log(this.name);
}
// 测试
f.printName();
f.alertName();
  • f本身没有alertName的属性,所以会去f的隐式原型__proto__中去寻找,f的隐式原型__proto__即为其构造函数Foo的显式原型prototype,Foo的显式原型已被扩展了alertName的属性,所以可顺利执行
  • this永远指向对象本身,在执行f.alertName()的时候会执行到第6行alert(this.name),但是这里的this还是f本身

原型链

  • f.toString() -> f.__proto__ -> Foo.prototype -> 无toString属性 -> Foo.prototype是一个对象 -> Foo.prototype.__proto__-> Object.prototype -> f.__proto__.__proto__
  • Object.prototype.__proto__ = null
// 构造函数
function Foo(name, age){
   
  this.name = name;
}
Foo.prototype.alertName = function () {
   
  alert(this.name);
}
// 创建实例 
var f = new Foo('zhangsan');
f.printName = function () {
   
  console.log(this.name);
}
// 测试
f.printName();
f.alertName();
f.toString(); // 要去f.__proto__.__proto__中查找

在这里插入图片描述

在这里插入图片描述

instanceof

  • 判断引用类型属于哪个构造函数的方法
  • f instanceof Foo判断逻辑:f__proto__一层一层往上,能否对应到Foo.prototype
  • f instanceof Object判断逻辑:f__proto__一层一层往上,是否对应到Object.prototype

循环对象自身属性

  • 从上述代码中可得f拥有三个属性:name、printName、alertName
  • 但我们往往希望拿到对象本身定义的属性,而不要来自其原型的属性
var item;
for(item in f){
   
  // 高级浏览器已经在for in中屏蔽了来自原型的属性
  // 但这里建议大家加上这个判断,保证程序的健壮性以满足浏览器的兼容性
  if(f.hasOwnProperty(item)){
   
    console.log(item)
  }
}

题目解答

  1. 如何准确判断一个变量是数组类型
var arr = []
arr instanceof Array //true
typeof arr //object,typeof是无法判断数组的
  1. 写一个原型链继承的例子
  • 面试千万不要这么写
  • 面试写更贴近实战的例子
// 动物
function Animal() {
   
  this.eat = function () {
   
    console.log('animal eat');
  }
}
// 狗
function Dog() {
   
  this.bark = function () {
   
    console.log('dog bark');
  }
}
Dog.prototype = new Animal();
// 哈士奇
var hashiqi = new Dog();
hashiqi.eat();
hashiqi.bark();
// 一个封装DOM查询的例子
function Elem(id) {
   
  this.elem = document.getElementById(id);
}

Elem.prototype.html = function (val) {
   
  var elem = this.elem;
  if (val) {
   
    elem.innerHTML = val;
    return this; // 链式操作
  } else {
   
    return elem.innerHTML
  }
}

Elem.prototype.on = function (type, fn) {
   
  var elem = this.elem;
  elem.addEventListener(type, fn);
  return this; // 链式操作
}

var div1 = new Elem('div1');
console.log(div1.html());
div1.html('<p>hello world</p>').on('click', function () {
   
  alert('clicked');
}).html('<p>javascript</p>'</
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值