函数
函数声明
//ES5
function getName(){
console.log('name')
}
getName() // name
//匿名函数
!function (){
console.log('none')
}(); // none
var getName5 = function() {
console.log('var')
}
getname() // var
//ES6
// 箭头函数
(() => console.log('=>'))() // =>
let getName6 = () => {
console.log('let =>')
}
getname() // let =>
箭头函数与普通函数区别
- 不能绑定
arguments
对象,在函数内无法访问arguments
对象,会报错 - 不能用作构造器,不能通过
new
来创建实例 - 默认不会创建
prototype
原型属性 - 不能作为
Generator()
函数,不能使用yeild
关键字
函数转换
隐式转换函数 toString()
和 valueOf()
toString()
函数会在打印函数的时候调用,如console.log()
valueOf
会在获取函数原始值时调用,如运算
原型
原型是 JavaScript 重要的特性之一
可以让对象从其他对象继承功能的特性
JavaScript 也被称之为 基于原型的语言
严格的说原型应该是对象的特性,但函数其实也是一种特殊的对象
function fn() {}
console.log(fn instanceof Object) // true
原型、原型链
原型就是对象属性
包括,隐式原型__proto__
属性和显式原型 prototype
属性
隐式原型__proto__
一般为对象自带的,最上级为 Object ,而 Object 的隐式原型 指向 null
let obj = {
name: 'obj'
}
console.log(obj.__proto__) // [Object: null prototype] {}
console.log(obj.__proto__.__proto__) // null
function fn () {}
console.log(fn.__proto__.__proto__) // [Object: null prototype] {}
console.log(fn.prototype) // {}
console.log(obj.prototype) // undefined
let p = {code:'p',name:'pn'}
let c = {
__proto__:p,name:'cn'
}
console.log(c.name) // cn
console.log(c.code) // p 通过原型链查找
// 可以使用 hasOwnProperty() 方法判断属性是否为本对象拥有
console.log(c.hasOwnProperty('name')) // true
console.log(c.hasOwnProperty('code')) // false
class P {}
let c = new P()
console.log(c.__proto__ === P.prototype) // true
new 操作符实现了什么?
function F(args){}
let f = new F(args)
- 创建一个临时空对象,让对象的隐式原型指向函数 F 的显式原型
- 执行函数 F(),将 this 指向临时对象,并传入参数 args 得到结果
- 判断结果如果结果为非空对象,返回 result,否则返回临时对象
function F(init) {
let a = { name: 'a' }
let b = function() {
console.log(init)
}
}
let args = ['test']
let fn = Object.create(F.prototype)
let obj = F.apply(fn,args)
let f1 = obj && typeof obj === 'object'? obj: fn
let f2 = new F('test')
console.log(f1.__proto__ === f2.__proto__) // true
console.log(f1.a === f2.a) // true
console.log(f1.b === f2.b) // true
原型链继承
function A() {}
A.prototype.a = function () {
console.log('a')
}
function B() {}
B.prototype = new A()
B.prototype.b = function() {
console.log('b')
}
let c = new B()
c.a() // a
c.b() // b
function fn() {}
console.log(fn.__proto__ === Function.prototype) // true
// 可以通过 instanceof 判断原型是否在原型链上
console.log(fn instanceof Function) // true
作用域
-
作用域是指赋值、取值的范围,即上下文,可以有效的防止变量/函数重复定义以及控制它们的访问属性
-
浏览器和 Node.js 作用域处理不同,比如全局作用域,浏览器会自动将未主动声明的变量提升到全局作用域,
Node.js 则是需要显示挂载到 global 对象上。比如在 ES6 之前,浏览器不提供模块级别的作用域,而 Node.js 的 CommonJS 模块提供模块级别作用域。
-
在类型上可以分为
全局作用域
(window/global)、块级作用域
(let、const、try/catch)、模块级作用域
(ES6 Module、CommonJS)、以及函数作用域
命名提升
对于使用 var
声明的变量,以及创建命名函数的时候, JavaScript 在解析执行的时候会将声明的内容提前提升到作用域顶部,这种机制称为命名提升
var a = test()
function test() {
return 'a'
}
console.log(a) // a
console.log(b) // undefined 只是声明还未赋值
var b = 'b'
console.log(b) // b
console.log(c) // 报错 Cannot access 'c' before initialization let 不会进行变量提升
let c = 'c'
f() // 报错 f is not a function 只是声明还未赋值
var f = function() {}
闭包
在函数内部访问外部函数作用域时就会产生闭包,闭包允许将函数与其操作的某些数据(环境)关联起来,这种关联不只是跨作用域引用,也可以实现数据与函数的隔离
// 通过闭包实现单例模式
let SingleStudent = (function(){
function Student() {}
let _student;
return function () {
if(_student) return _student;
_student = new Student()
return _student
}
})()
let s1 = new SingleStudent()
let s2 = new SingleStudent()
console.log(s1 === s2)
注意,闭包不一定会引起内存泄漏,可参考