JS相关知识笔记

数据类型

基本类型

  • String:任意字符串
  • Number:任意数字
  • Boolean:true/false
  • Undefined:undefined
  • Null:null

对象(引用类型)

  • Oject:任意对象
  • Function:函数,一种特别的对象(可以执行)
  • Array:数组,一种特别的对象(数值下标,内部数据是有序的)

判断

  • typeof:返回数据类型的字符串表达;let a; console.log( a,typeof a,typeof a === 'undefined' ,a===undefined);//undefined,'undefined',true,true;可以判断undefined、数值、字符串、布尔类型、函数;但是它不能判断null和object,object和array
  • instanceof :判断对象的具体类型;
  • ===:判断undefined和null,因为undefined和null原本就只有一个值

小知识

let b1 = {
    
    
	b2:[1,'abc',console.log],
	b3:function(){
    
    
		console.log('b3');
		return function () {
    
    
			return 'study'
		}
	}
}
b1.b3()
console.log(b1 instanceof Object,b1 instanceof Array) // true  false
console.log(b1.b2 instanceof Array,b1.b2 instanceof Object) // true  true
console.log(b1.b3 instanceof Function,b1.b3 instanceof Object) // true  true
console.log(typeof b1.b3 === 'function') // true
console.log(typeof b1.b2[2] === 'function') // true
b1.b2[2](4) //4
b1.b3()// function () { return 'study' }
b1.b3()() // study

b1.b2[2]既然是函数,就可以执行调用,加括号即可;反之,如果右边加了括号,那么左边表达式就是一个函数

相关问题

什么是实例?

类型:类型对象,以下例子中,构造的Person函数就是一个类型对象
实例:实例对象,以下例子中,p就是根据Person构造函数这个类型对象创建的实例对象
(其实定义Person对象时并不知道它时构造函数,而是在new的时候才定义为其构造函数)

function Person (name,age){
    
    
	this.name = name;
	this,age = age;
}//构造函数-》类型-》对象(函数是一个特别的对象)
let p = new Person() 

三个问题

  1. undefined和null的区别
    undefined代表定义未赋值,null代表定义了,只是值为null
  2. 什么时候给变量赋值为null
    初始赋值时,为了表明将要赋值为对象;
    结束前,让对象成为垃圾对象(被垃圾回收器回收)
  3. 严格区别变量类型与数据类型
    数据类型分为基本类型和对象类型
    变量类型分为基本类型和引用类型;基本类型保存的是基本类型数据,而引用类型保存的是地址值

数据—变量—内存

什么是数据?

  • 存储在内存中代表特定信息的东西,本质上是0101…
  • 数据的特点:可传递,可读写,可运算
    什么是内存?
  • 内存条充电后产生的可存储数据的空间(临时的);
  • 内存条的产生和消亡:内存条(电路板)——》通电——》产生内存空间——》存储数据——》处理数据——》断电——》内存空间和数据都消失);
    一块小内存的2个数据:内部存储的数据,地址值;
  • 内存分类:栈:全局变量/局部变量,堆:对象。var obj = {name:'Tom'}栈空间保存的是obj,堆空间保存的是name:‘Tom’。对于函数来说,函数本质上是一个对象,因此函数在堆空间中,而函数名本质上是一个变量,因此函数名在栈空间中
    什么是变量?
  • 可变化的量,由变量名和变量值组成;
  • 每个变量都对应的一块小内存,变量名用来查找对应的内存,变量值就是内存中保存的数据;
  • 变量赋值是将一个变量保存的内容拷贝到另一个变量中var obj = {name:'Tom'}; var a = obj,其中obj保存的是堆内存中name的地址,比如这个地址为0x123,那么此时var a = obj会将obj中保存的地址拷贝一份到a。a此时为ox123
    数据,内存,变量之间的关系
  • 内存是用来存储数据的空间
  • 变量是内存的标识(查找内存空间)

关于赋值和内存的问题
问题:var a = xxx,a内存中保存的是什么?
答:如果xxx是基本数据,保存的就是这个数据;如果xxx是对象,保存的就是这个对象的地址值;如果xxx是一个变量,保存的xxx的内存内容(可能是基本数据,也可能是地址值)
关于引用变量赋值问题

  • 只要2个变量保存的是同一个对象的地址值,那么这2个引用变量指向同一个对象;通过一个变量修改对象内部数据,另一个变量看到的是修改之后的数据;
  • 2个引用变量指向同一个对象,让其中一个引用变量指向另一个对象,另一个引用变量依然指向前一个对象

在js调用函数时传递变量参数时,是值传递还是引用传递

let b = 3;
function fn(a){
    
    
	a = a + 1;
	console.log(a);//4
}
fn(b);
console.log(b)//3

a只是获取了b的值,并没有对b进行修改

var pro = {
    
    name: 'Sara'}
function fn2(obj) {
    
    
    obj.name = 'Tom'
}
fn2(pro)
console.log(pro.name)

obj获取的是pro这个对象的地址值,obj和pro同时指向同一个地址,
obj.name实际上与pro.name是同一个,所以修改了obj,pro也会同时被修改

在函数传参的时候实际上是做了两个步骤,先从栈中获取实参对象的值,然后做一个赋值操作,将值赋给形参。在获取基本类型的时候,是从栈中取得基本类型的值,而获取对象类型的时候,从栈中得到的是这个对象类型所指向的地址,然后由这个地址去堆中寻找到对象内属性的值。

所以,对这个问题可以有着两种理解,第一种则是理解为两个都是值传递,传递的都是这个变量的内容,只是这个变量的内容可以是基本数据,也可以是地址数据。第二种则是把传递的地址值看作是引用传递,不过一般来说都当做是值传递。

JS引擎如何管理内存?

  1. 内存生命周期
    分配空间,得到它的使用权——>存储数据,可以反复进行操作——>释放小内存空间
var a = 3;
var obj = {
    
    };
function fn () {
    
    
	var b = {
    
    };
}
fn()

局部变量在函数执行时产生,在函数执行完后自动释放,因此,b是自动释放,而b所指向的对象是在后面的某个时刻由垃圾回收器回收
2. 释放内存
局部变量:函数执行完成后自动释放;
对象:成为垃圾对象——>垃圾回收器回收

对象

  1. 什么是对象?
    多个数据的封装体(用来保存多个数据的容器);一个对象代表现实世界中的一个事物

  2. 为什么要用对象?
    统一管理多个数据

  3. 对象的组成
    属性,方法;属性由属性名(字符串)和属性值(任意类型)组成,方法是一种特别的属性,属性值为函数的属性为方法。

  4. 如何访问对象内部数据
    方式一:对象.属性名;编码简单,但有时不能用
    方式二:对象[‘属性名’];编码复杂,但是能通用

  5. 什么时候必须用[‘属性名’]的方式?
    属性名包含特殊字符:- 空格
    变量名不确定

var p = {
    
    };
//1.给p对象添加一个属性:content-type : 'text/json'
p.content-type = 'text/json'//报错
p[content-type] = 'text/json'

//2.属性名不确定
let propName = 'myAge';
let value = 18;
p.propName = value;//报错
p[propName] = value;

函数

  • 什么是函数?
    实现特定功能的n条语句的封装体;只有函数是可以执行的,其他类型的数据不可以执行
  • 为什么要用函数?
    提高代码复用;便于阅读交流
  • 如何定义函数
    函数声明和函数表达式
//函数声明
function fn1(){
    
    
	console.log(1);
}
//函数表达式
let fn2 = function(){
    
    
	console.log(2);
}
  • 如何调用(执行)函数
    test():直接调用;
    obj.test():通过对象调用;
    new test():new调用;
    test.call/apply(obj):临时让test成为obj的方法进行调用
var obj = {
    
    };
function test2 (){
    
    
	this.xxx = 'wewe'
}
obj.test();//不能直接调用,根本没有
test2.call(obj);//可以让一个函数成为指定任意对象的方法来调用

回调函数

什么函数是回调函数

  • 你定义的
  • 你没有调
  • 但最终它执行了

常见的回调函数

  • DOM事件回调函数document.getElementById('btn') = function(){alert(1);}
  • 定时器回调函数
  • ajax回调函数
  • 生命周期回调函数

_IIFE(立即执行函数)

理解

函数要么定义,要么执行;定义就是给函数命名,有两种方式,函数声明function fun(){alert(1);}和函数表达式let fun = function(){alert(1);}。执行就是在函数后面加(),括号前面一定要是个整体,因此立即执行函数为(function(){alert(1)})();也称为匿名函数自调用

作用

  • 隐藏实现
  • 不会污染外部(全局)命名空间
  • 用它编写JS模块

函数中的this

function Person(color) {
    
    
	console.log(this);
	this.color = color;
	this.getColor = function (){
    
    
		console.log(this);
		this.color = color;
	};
	this.setColor = function (color){
    
    
		console.log(this);
		this.color = color;
	};
}
Person('red');//this是window
let p = new Person('yellow');//this是p,因为window调用完(new Person),赋值给p
p.getColor();//this是p,p调用的

let obj = {
    
    };
p.setColor.call(obj,'black');//this是obj

let test = p.setColor;
test();//window,函数是直接调用的

Person(‘red’):this是window;执行结果为:打印出第一个console:window;
let p = new Person(‘yellow’):this为p,执行结果为:打印出第一个console;其实本质上还是window调用的,但是调用完之后赋值给p了
p.getColor():this为p,谁调用this是谁

this是什么?

  • 任何函数本质上都是通过某个对象来调用的,如果没有明确指定就是window
  • 所有函数内部都有一个变量this
  • 它的值是调用函数的当前对象

如何确定this的值

  • test():window
  • p.test():p
  • new test():新创建的对象
  • p.call(obj):obj

函数的prototype

原型和原型链的理解
在这里插入图片描述

函数的原型对象

每当新对象(a,b,c)被创建时,除了各自的属性以外还有一个隐式的__proto__属性被创建,这个属性会指向各自的原型对象,而这个原型对象还会有自己的__proto__属性指向他们的原型对象,最终会指向Object,Object是所有对象的基础。Object中的proto属性指向null。原型对象在被创建的时候,会自动获得一个属性constructor,constructor中的name属性显示了创建abc的对象。

abc有__proto__属性指向原型对象,而构造abc的对象有prototype属性也指向原型对象。原型对象有constructor属性指向构造abc的对象。

let a = new String('abc');
let b = new Number(666);
let c = new Object();

在这里插入图片描述
在这里插入图片描述

函数的prototype属性

  • 每个函数都有一个prototype属性,它默认指向一个Object空对象(即称为:原型对象);空对象即没有我们自己定义的属性
  • 原型对象中有一个属性constructor,它指向函数对象console.log(Date.prototype.constructor === Date)//true
  • 构造函数跟他的原型对象相互引用

给原型对象中添加属性(一般都是方法)

作用:函数的所有实例对象自动拥有原型中的属性(方法)。给原型对象添加属性=》实例对象可以访问

function Fun(){
    
    };
Fun.prototype.test = function(){
    
    
	console.log('test()')
}
let fun = new Fun();
fun.test();//test()

显示原型和隐式原型

  • 每个函数function都有一个prototype属性,即显式原型(属性)function Fn(){};console.log(Fn.prototype),函数的显示原型指向的对象(原型对象):默认是空Object实例对象;但是Object不满足
  • 每个实例对象都有一个__proto__,可称为隐式原型(属性);let fn = new Fn();console.log(fn.__proto__)
  • 实例对象的隐式原型的值为其对应构造函数的显示原型的值;console.log(Fn.prototype === fn.__proto__)//true
  • 函数的prototype属性:定义函数时自动添加的,默认值是一个空Object对象function Fn(){};内部语句为:this.prototype = {}
  • 对象的_proto_属性:创建对象时自动添加的,默认值为构造函数的prototype属性值let fn = new Fn();其实内部语句为:this.__proto__ = Fn.prototype
  • 对于函数而言也可以看做Function的实例,对于function Foo(){}来说,相当于let Foo = new Function()此时Foo作为实例对象,具有__proto__属性,并且这个属性指向构造它的Function的prototype属性。即Foo.__proto__ = Function.prototype。因此,所有函数的__proto__都是一样的,都指向Function的显示原型
  • 所有函数都是Function的实例(包括Function)Function.__proto__ == Function.prototype
  • Object的原型对象是原型链的尽头,因为他的隐式原型为null

在这里插入图片描述

  1. 定义构造函数
  2. 创建实例对象
  3. 给原型添加方法(显示原型可以添加方法,隐式原型添加不了,ES6以后才可能添加)
  4. 通过实例对象调用原型的方法

原型链

访问一个对象的属性时,现在自身属性中查找,找到返回;如果没有,在沿着__proto__这条链向上查找,找到返回;如果最终没有找到,返回undefined。(隐式原型链:按照隐式原型去找的);

原型链的作用:查找对象的属性(方法)

function Fn(){
    
    
	this.test1 = function(){
    
    
		console.log('test1()');
	}
}
Fn.prototype.test2 = function(){
    
    
	console.log('test2()');
}
let fn = new Fn();
fn.test1();
fn.test2();
console.log(fn.toString());
console.log(fn.test3)//undefined
fn.test3();//test3 is not function,将test3当做一个函数去执行,而undefined并不是函数,因此会报错

在这里插入图片描述

  1. 创建了函数对象Fn,该函数对象在堆空间中;而函数名在栈空间中。相当于一个赋值操作,堆空间中的对象先产生,赋值给栈空间的变量名;
  2. Fn函数对象中含有显示原型属性prototype,该属性指向一个object空对象,空对象的意思是没有我们自己定义的属性和方法,之后定义了以后才会有;
  3. object空对象是Object的实例对象,既然是实例对象,那么必然就有一个隐式原型属性__proto__。Object函数对象在一开始就存在(万物之源),因为每一个函数都会有一个显示原型属性,因此Object同样含有显示原型属性prototype,该显示原型属性等于Object的实例对象即object空对象中的__proto__属性。
  4. Object作为一个构造函数,拥有一个Object原型对象,该原型对象有js内置的一些方法,以及__proto__属性,该属性为null
  5. let fn = new Fn()在栈空间创建一个变量fn,在堆空间创建一个Fn的实例对象,该实例对象含有隐式原型属性__proto__,该属性存放的是其构造函数显示原型的值,显示原型属性,隐式原型属性均指向object空对象

原型链属性问题

  • 读取对象的属性值时,会自动到原型链中查找;
  • 设置对象的属性值时,不会查找原型链,如果当前对象中没有此属性,直接添加此属性并设置其值;
  • 方法一般定义在原型中,属性一般通过构造函数定义在对象本身上
function Fn(){
    
    
}
Fn.prototype.a = 'xxx';
let fn1 = new Fn();
console.log(fn1.a)//xxx
let fn2 = new Fn();
fn2.a = 'yyy';
console.log(fn1.a); //xxx
console.log(fn2.a);//yyy

instanceof

  1. instanceof是如何判断的?
    表达式:A instanceof B
    如果B函数的显式原型对象在A对象的原型链上,返回true,否则返回false

所以函数的原型对象默认是一个空的Object的实例对象,Object除外

console.log(Object instanceof Function)//true
console.log(Object instanceof Object)//true
console.log(Function instanceof Function)//true
console.log(Function instanceof Object)//true

原型面试题

function A (){
    
    
}
A.prototype.n = 1;
let b = new A();
A.prototype = {
    
    
	n:2,
	m:3
}
let c = new A()
console.log(b.n,b.m,c.n,c.m)//1,undefined,2,3

在这里插入图片描述

let F = function(){
    
    }
Object.prototype.a = function(){
    
    
	console.log('a()');
}
Function.prototype.b = function(){
    
    
	console.log('b()');
}
let f = new F();
f.a();//a()
f.b();//报错
F.a();//a()
F.b()//b()

在这里插入图片描述

变量提升和函数提升

变量声明提升

  • 通过var定义的(声明)的变量,在定义语句之前就可以访问到
  • 值:undefined;
var a = 3;
function fn(){
    
    
	console.log(a);
	var a = 4;
}
fn()

相当于:

var a = 3;
function fn(){
    
    
	var a;
	console.log(a);
	a = 4;
}
fn()

所以输出undefined

函数声明提升

  • 通过function声明的函数,在之前就可以直接调用
  • 值:函数定义(对象)
fn2();//可调用
fn3();//不可调用,这是变量提升,报错
function fn2(){
    
    
	console.log('fn2()');
}
var fun3 = function(){
    
    
	console.log('fn3()')
}

执行上下文

全局执行上下文

  • 在执行全局代码前将window确定为全局执行上下文
  • 对全局数据进行预处理:1.var定义的全局变量——》undefined,添加为window属性;2.function声明的全局函数——》赋值(fun),添加为window的方法;3.this——》赋值(window)。
  • 开始执行全局代码

函数执行上下文

  • 在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象
  • 对局部数据进行预处理:1.形参变量——》赋值为实参数据——》添加为函数执行上下文的属性;2.arguments——》赋值(实参列表),添加为执行上下文属性;3.var定义的局部变量——》undefined,添加为执行上下文的属性;4.function声明的函数——》赋值(fun),添加为执行上下文的方法;4.this——》赋值(调用函数的对象)。
  • 开始执行函数体代码

局部变量是放在栈中的,而现在是将函数的局部变量放在栈中的一片封闭的区域中

function fn(a1){
    
    
	console.log(a1);//2
	console.log(a2);//undefined
	a3(); //a3()
	console.log(this); //window
	console.log(arguments);//伪数组:(2,3)
	var a2 = 3;
	function a3(){
    
    
		console.log('a3()');
	}
}
fn(2,3)

执行上下文题

console.log('gb:' + i);
var i = 1;
foo(1);
function foo(i){
    
    
 if(i == 4){
    
    
	return;
 }
 console.log('fb:' + i);
 i=i+1;
 foo(i);
 console.log('fe:' + i);
}
console.log('ge:' + i)

依次输出:
gb:undefined
fb:1
fb:2
fb:3
fe:3
fe:2
fe:1
ge:1
整个过程产生了5个执行上下文。

function a(){
    
    };
var a;
console.log(typeof a);

先执行变量提升再执行函数提升;因此输出function

if(!(b in window)){
    
    
 var b = 1;
}
console.log(b);//undefined

b in window为true,因此进不了if语句内部,所以b为undefined。

var c = 1;
function c(c){
    
    
	console.log(c);
}
c(2);//报错

实际为:

var c;
function c(c){
    
    
	console.log(c);
	var c = 3
};
c = 1;
c(2);

首先存在变量提升和函数提升,函数function c在提升时已经执行好了;之后又将1赋值给c ,此时c已经不是函数了;因此会报错,实际上函数c里面的代码根本不会执行

作用域与执行上下文

区别

  • 全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了,而不是在函数调用时;而全局执行上下文环境是在全局作用域确定之后,js代码马上执行之前创建的;函数执行上下文环境是在调用函数时,函数体代码执行之前创建。
  • 作用域是静态的,只要函数定义好了就一直存在,且不会在变化;而执行上下文是动态的,调用函数时创建,函数调用结束时上下文环境就会被释放。

联系

  • 执行上下文环境(对象)是从属于所在的作用域
  • 全局上下文环境==》全局作用域
  • 函数上下文环境==》对应的函数作用域

练习

var x = 10;
function fn(){
    
    
	console.log(x);
}
function show(f){
    
    
	var x = 20;
	f();
}
show(fn);//10

解析:相当于有三个作用域,全局作用域,fn作用域,show作用域;其中show函数中的f()相当于执行fn,在函数fn中没有找到对应的x,此时就需要去全局作用域中去寻找,所以x = 10.

var fn = function(){
    
    
	console.log(fn);
}
fn();
var obj = {
    
    
	fn2: function(){
    
    
		console.log(fn2)
	}
}
obj.fn2()

解析:fn()相当于 function(){console.log(fn)}();console.log(fn),因此输出 function(){console.log(fn)}();
obj.fn2()相当于 function(){console.log(fn2)}();console.log(fn2),在function内部也就是{}中是没有找到fn2,因此就会去全局找,全局也没有,故而报错。

ES5里只有全局作用域个函数作用域,这便是在全局作用域中,只有函数具有切割作用域的功能;所以在这里的obj后面的括号是没有切割作用域的功能的。fn2这个函数作用域往外走直接就看到的是全局作用域,所以此时,上下文视野里只有obj和上面的fn两个变量,是找不到fn2的,所以报错。

如果想要不报错,fn2函数里的输出应写为console.log(this.fn2)或者console.log(obj.fn2)

猜你喜欢

转载自blog.csdn.net/weixin_48242257/article/details/121989248
今日推荐