七种数据类型口诀:
USONB you are so niubility(JavaScript七种数据类型)
U undefined
S string symbol
O object
N null number
B boolean
基本(值)类型
基本数据类型数值判断方法Number任意数值typeofString任意字符串typeofBooleantrue/falsetypeofundefinedundefinedtypeof / ===nullnull===注:typeof 返回数据类型的字符串表达
undefined、null两个类型只有一个值。
var a
console.log(a, typeof a, typeof a==='undefined',a===undefined )
// undefined 'undefined' true true
对象(引用)类型
对象(引用)类型判断方法Objecttypeof/instanceofArrayinstanceofFunctiontypeofArray是一种特别的对象,它的属性是数值下标,内部数据是有序的
Function是一种特别的对象,Function可以执行,但普通Object对象不行
强调:
-
typeof:可以判断: undefined / 数值 / 字符串 / 布尔值 / function
不能判断: null与object object与array
var a = null
console.log(typeof a, a===null) // typeof a 的返回值为 ‘object’
-
instanceof:判断对象的具体类型
-
===:可以判断: undefined,null
var b1 = {
b2: [1, 'abc', console.log],
b3: function () {
console.log('b3')
return function () {
return 'xfzhang'
}
}
}
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.b2, '-------') // 'object'
console.log(typeof b1.b3==='function') // true
相关问题:
- undefined与null的区别?
- undefined 代表定义未赋值
- null 定义并赋值了,只是值为null
- 什么时候给变量赋值为null呢?
- 初始赋值,表明将要赋值为对象
- 结束前,让对象成为垃圾对象(被垃圾回收器回收)
如何访问对象内部数据?
.属性名
:编码简单,有时不能用- [‘属性名’]:编码麻烦,能通用
//1. 给p对象添加一个属性: content type: text/json
// p.content-type = 'text/json' // 不能用,报错
p['content-type'] = 'text/json'
console.log(p['content-type'])
//2. 属性名不确定
var propName = 'myAge'
var value = 18
// p.propName = value //不能用,报错
p[propName] = value
console.log(p[propName])
3. 函数
如何调用函数?
- test():直接调用
- obj.test():通过对象调用
- new test():new调用
- test.call(obj)或test.apply(obj):临时让test成为obj的方法进行调用(可以让一个函数成为指定任意对象的方法进行调用)
var obj = {}
function test () {
this.xxx = 'scorpios'
}
// obj.test() 不能直接, 根本就没有test方法
test.call(obj) // 相当于 obj.test()
console.log(obj.xxx)
test.apply(obj)
console.log(obj.xxx)
-
函数也是对象
- instanceof Object === true
- 函数有属性:prototype
- 函数有方法:call()/apply()
- 可以添加新的属性/方法
-
函数中的this()
- 显式指定谁:obj.xxx()
- 通过call/apply指定谁调用:xxx.call(obj)
- 不指定谁调用:xxx() : window
- 回调函数:看背后是通过谁来调用的:window/其它
注:任何函数本质上都是通过某个对象来调用的,如果没有直接指定就是window
-
匿名函数自调用:
(function(w, obj){ //实现代码 })(window, obj)
专业术语为: IIFE (Immediately Invoked Function Expression) 立即调用函数表达式
IIFE的作用:1、隐藏实现 2、不会污染外部(全局)命名空间 3、用它来编码js模块
-
回调函数的理解,什么函数才是回调函数?
满足三个条件:1、你定义的 2、你没有调用 3、但它最终执行了(在一定条件下或某个时刻)
- 函数的prototype属性
-
每个函数都有一个prototype属性,它默认指向一个Object空对象(即称为:原型对象)
-
原型对象中有一个属性constructor,它指向函数对象
- 给原型对象添加属性(一般都是方法)
- 作用:函数的所有实例对象自动拥有原型中的属性(方法)
// 每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象)
console.log(Date.prototype, typeof Date.prototype)
function Fun () {}
console.log(Fun.prototype) // 默认指向一个Object空对象(没有我们的属性)
// 原型对象中有一个属性constructor, 它指向函数对象
console.log(Date.prototype.constructor===Date) // true
console.log(Fun.prototype.constructor===Fun) // true
//给原型对象添加属性(一般是方法) ===> 实例对象可以访问
Fun.prototype.test = function () {
console.log('test()')
}
var fun = new Fun()
fun.test()
补充:构造函数与普通函数的区别:
- 构造函数也是一个普通函数,创建方式和普通函数一样
- 普通函数调用方式:直接调用person()
- 构造函数调用方式:需要使用new关键字来调用 new person()
// 构造函数也是一个普通函数,创建方式和普通函数一样。
function Foo(){}
Foo();//普通函数调用方式
var f = new Foo();//构造函数调用方式
2. 显示原型属性和隐式原型属性
-
所有函数都有一个特别的属性:
prototype
显式原型属性 -
所有实例对象都有一个特别的属性:
__proto__
隐式原型属性 -
显式原型属性与隐式原型属性的关系
- 对象的隐式原型的值为其对应构造函数的显式原型的值
//定义构造函数 function Fn() {} // 1. 每个函数function都有一个prototype,即显式原型属性, 默认指向一个空的Object对象 console.log(Fn.prototype) // 2. 每个实例对象都有一个__proto__,可称为隐式原型 //创建实例对象 var fn = new Fn() console.log(fn.__proto__) // 3. 对象的隐式原型的值为其对应构造函数的显式原型的值 console.log(Fn.prototype===fn.__proto__) // true //给原型添加方法 Fn.prototype.test = function () { console.log('test()') } //通过实例调用原型的方法 fn.test()
- 对象的隐式原型的值为其对应构造函数的显式原型的值
-
总结:
- 函数的prototype属性:在定义函数时自动添加的,默认值是一个空Object对象
- 对象的
__proto__
属性:创建对象时自动添加的,默认值为构造函数的prototype属性值 - 程序员能直接操作显式原型,但不能直接操作隐式原型(ES6之前)
-
原型链
访问一个对象的属性时,先在自身属性中查找,找到返回;如果没有,再沿着__proto__
这条链向上查找,找到返回;如果最终没找到,返回undefined
// 1. 函数的显示原型指向的对象默认是空Object实例对象(但Object不满足)
console.log(Fn.prototype instanceof Object) // true
console.log(Object.prototype instanceof Object) // false
console.log(Function.prototype instanceof Object) // true
// 2. 所有函数都是Function的实例(包含Function)
console.log(Function.__proto__===Function.prototype)
// 3. Object的原型对象是原型链尽头
console.log(Object.prototype.__proto__) // null
原型链-属性问题:
- 读取对象的属性值时:会自动到原型链中查找
- 设置对象的属性值时:不会查找原型链, 如果当前对象中没有此属性, 直接添加此属性并设置其值
- 方法一般定义在原型中,属性一般通过构造函数定义在对象本身上
function Fn() {}
Fn.prototype.a = 'xxx'
var fn1 = new Fn()
console.log(fn1.a) // xxx
var fn2 = new Fn()
fn2.a = 'yyy'
console.log(fn1.a, fn2.a) // xxx yyy
3. 执行上下文与执行上下文栈
-
变量提升与函数提升
-
变量提升:在变量定义语句之前,就可以访问到这个变量(undefined)
-
函数提升:在函数定义语句之前,就执行该函数
-
先有变量提升,再有函数提升
函数提升必须使用声明的方式
fn2() //可调用 函数提升 fn3() //不能这样使用 变量提升 // 声明方式 function fn2() { console.log('fn2()') } // 变量方式 var fn3 = function () { console.log('fn3()') }
-
-
全局执行上下文
- 在执行全局代码前将window确定为全局执行上下文
- 对全局数据进行预处理
- var定义的全局变量 ==> undefined,添加为window的属性
- function声明的全局函数 ==> 赋值(fun),添加为window的方法
- this ==> 赋值(window)
- 开始执行全局代码
- 函数执行上下文
- 在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象(虚拟的,存在于栈中)
- 对局部数据进行预处理
- 形参变量 ==> 赋值(实参) ==> 添加为执行上下文的属性
- arguments==>赋值(实参列表),添加为执行上下文的属性
- var定义的局部变量==>undefined,添加为执行上下文的属性
- function声明的函数 ==>赋值(fun),添加为执行上下文的方法
- this==>赋值(调用函数的对象)
- 开始执行函数体代码
-
生命周期
- 全局 : 准备执行全局代码前产生, 当页面刷新/关闭页面时死亡
- 函数 : 调用函数时产生, 函数执行完时死亡
-
包含哪些属性:
- 全局 :
- 用var定义的全局变量 ==> undefined
- 使用function声明的函数 ===> function
- this ===> window
- 函数
- 用var定义的局部变量 ==> undefined
- 使用function声明的函数 ===> function
- this ===> 调用函数的对象, 如果没有指定就是window
- 形参变量 ===> 对应实参值
- arguments ===> 实参列表的伪数组
- 全局 :
-
执行上下文创建和初始化的过程
- 全局:
- 在全局代码执行前最先创建一个全局执行上下文(window)
- 收集一些全局变量, 并初始化
- 将这些变量设置为window的属性
- 函数:
- 在调用函数时, 在执行函数体之前先创建一个函数执行上下文
- 收集一些局部变量, 并初始化
- 将这些变量设置为执行上下文的属性
- 全局:
-
如何产生闭包?
当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包
注意: 闭包存在于嵌套的内部函数中
-
产生闭包的条件?
1、函数嵌套 2、内部函数引用了外部函数的数据(变量/函数)
-
闭包作用:
- 使用函数内部的变量在函数执行完后, 仍然存活在内存中(延长了局部变量的生命周期)
- 让函数外部可以操作(读写)到函数内部的数据(变量/函数)
问题:
- 函数执行完后, 函数内部声明的局部变量是否还存在? 一般是不存在, 存在于闭中的变量才可能存在
- 在函数外部能直接访问函数内部的局部变量吗? 不能,但我们可以通过闭包让外部操作它
-
写一个闭包程序
function fn1() { var a = 2 console.log(a) } console.log(a) // 报错,因为a没有定义,在函数外部无法操作函数内部的数据 fn1() function fn2() { var a = 2; function fn3() { a++; console.log(a); } return fn3; } var f = fn2(); f();// 在函数外部可以操作函数内部的数据 f()
-
闭包应用:
-
模块化:封装一些数据以及操作数据的函数, 向外暴露一些行为
function myModule() { //私有数据 var msg = 'My Scorpios' //操作数据的函数 function doSomething() { console.log('doSomething() '+msg.toUpperCase()) } function doOtherthing () { console.log('doOtherthing() '+msg.toLowerCase()) } //向外暴露对象(给外部使用的方法) return { doSomething: doSomething, doOtherthing: doOtherthing } } (function () { //私有数据 var msg = 'My Scorpios' //操作数据的函数 function doSomething() { console.log('doSomething() '+msg.toUpperCase()) } function doOtherthing () { console.log('doOtherthing() '+msg.toLowerCase()) } //向外暴露对象(给外部使用的方法) window.myModule2 = { doSomething: doSomething, doOtherthing: doOtherthing } })()
-
-
缺点:
- 变量占用内存的时间可能会过长
- 可能导致内存泄露
- 解决:及时释放f = null; //让内部函数对象成为垃圾对象
-
Object构造函数模式
var obj = {}; // 先创建空Object对象,此时内部数据是不确定的 obj.name = 'Tom' obj.setName = function(name){this.name=name}
问题:语句太多
-
对象字面量模式
var obj = { name : 'Tom', setName : function(name){this.name = name} }
问题:如果创建多个对象,有重复代码
-
工厂模式
function createPerson(name, age) { //返回一个对象的函数===>工厂函数 var obj = { name: name, age: age, setName: function (name) { this.name = name } } return obj } // 创建2个人 var p1 = createPerson('Tom', 12)
问题:对象没有一个具体的类型,都是Object类型
-
构造函数模式
function Person(name, age) { this.name = name; this.age = age; this.setName = function(name){this.name=name;}; } new Person('tom', 12);
问题:每个对象都有相同的数据,浪费内存
-
构造函数+原型的组合模式
function Person(name, age) { this.name = name; this.age = age; } Person.prototype.setName = function(name){this.name=name;}; new Person('tom', 12);
- 原型链继承 : 得到方法
function Parent(){} Parent.prototype.test = function(){}; function Child(){} Child.prototype = new Parent(); // 子类型的原型指向父类型实例 Child.prototype.constructor = Child var child = new Child(); //有test()
- 借用构造函数 : 得到属性
function Parent(xxx){this.xxx = xxx} Parent.prototype.test = function(){}; function Child(xxx,yyy){ Parent.call(this, xxx);//借用构造函数 this.Parent(xxx) } var child = new Child('a', 'b'); //child.xxx为'a', 但child没有test()
- 组合
function Parent(xxx){this.xxx = xxx} Parent.prototype.test = function(){}; function Child(xxx,yyy){ Parent.call(this, xxx);//借用构造函数 this.Parent(xxx) } Child.prototype = new Parent(); //得到test() var child = new Child(); //child.xxx为'a', 也有test()
- new一个对象背后做了些什么?
- 创建一个空对象
- 给对象设置
__proto__
,值为构造函数对象的prototype属性值this.__proto__
= Fn.prototype - 执行构造函数体(给对象添加属性/方法)