关于js函数的一些剖析 如bind call apply 原型链 作用域 作用域链 闭包的 函数提升的理解
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script type="text/javascript">
// 函数作用域的例子理解
console.log(aa(1)) // 1
function aa(n) {
return n * n
}
// console.log(bb) // Cannot access 'bb' before initialization
// console.log(bb(2)); // Cannot access 'bb' before initialization
var bb = function(n) {
return n * n;
}
console.log(bb(2)) // 4
// 2.函数提升仅适用于函数声明,而不适用于函数表达式 由上面的例子得出结论
// 3.函数表达式也可以提供函数名,并且可以用于在函数内部代指其本身
var cc = function fn(n) {
return n<2 ? 1 : n*fn(n-1)
}
console.log(cc(3)) // 6 fn(3) => 3*fn(2) => 3*2*fn(1) => 3*2*1 = 6
// 嵌套的函数
function getNum() {
var num1 = 6,
num2 = 8;
function add() {
return num1 + num2
}
return add();
}
// console.log(num1,num2) // num1 num2 is not defined =>得出 在函数内定义的变量不能在函数之外的任何地方访问
console.log(getNum()) // 14 =>得出 在另一个函数中定义的函数也可以访问在其父函数中定义的 所有变量和父函数有权访问的任何其他变量
var a = 2 , b = 4;
function multiply() {
return a * b;
}
console.log(multiply()) // 8 => 得出 定义在全局域中的函数可以访问所有定义在全局域中的变量
// 调用自身的函数我们称之为递归函数
function nodeTree(node) {
if (node == null){
return;
}
// do something with node
for (var i = 0; i < node.childNodes.length; i++) {
nodeTree(node.childNodes[i]);
}
}
// 箭头函数 箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this
// 箭头函数不能用作构造器,和 new一起用会抛出错误
// 箭头函数没有prototype属性
// yield 关键字通常不能在箭头函数中使用(除非是嵌套在允许使用的函数内)
var elements = [
'ssssss',
'ssssss',
'ssssss',
'ssssss'
];
// 当箭头函数只有一个参数时,可以省略参数的圆括号
const ele = elements.map(e => {
return e.length;
});
// 当箭头函数的函数体只有一个 `return` 语句时,可以省略 `return` 关键字和方法体的花括号
const eles = elements.map(e => e.length);
console.log(ele,eles) // 返回数组 [6, 6, 6, 6] [6, 6, 6, 6]
// call apply 调用函数的区别以及bind的作用:1.参数较少用call,参数较多用apply 2.传参方式不一样 call正常传 apply传数组
function fn(a,b){
console.log(this)
console.log(a+b)
}
//call调用函数,第一个参数用来改变this指向,除了第一个参数外的参数作为实参
fn.call({name:'test-call'},10,20) // {name: "test-call"} 30
// apply第二个参数为实参列表,调用会平铺开作为参数 apply传的是数组
fn.apply({name:'test-apply'},[10,20]) // {name: "test-apply"} 30
//作用:创建并返回一个新的函数,新函数跟fn函数长得一模一样,新函数的this指向被固定成了参数thisArg,并不会主动调用函数
var newFn=fn.bind(this)
console.log(newFn) // 返回 fn 场景,改变定时器中的this 重新绑定上下文 类似 var _this/that = this;这个操作
// 构造函数的调用 这里扯到函数的调用分为 call、apply 、构造函数的调用以及函数普通的调用 constr()
function constr(){
console.log(this,'111111') // constr {} "111111"
this.a = 1
};
var so = new constr();
console.log(so) // constr {a: 1}
// 函数的getter和setter
var newConstr = {
_name: '',
get name() {
return this._name
},
set name(newName) {
this._name = newName
}
}
// 测试 get set基本用法
console.log(newConstr.name) // 输出 --> ''
newConstr.name = 'set了一个值';
console.log(newConstr.name) // 输出 --> set了一个值
// Object.defineProperty的使用
var testConstr = function() {
var _name = '';
var obj = {};
Object.defineProperty(obj, 'name', {
configurable: true,
enumerable: true,
get: function() {
return _name;
},
set: function(newName) {
_name = newName;
}
})
return obj;
}();
testConstr.name = "使用Object.defineProperty方法set了一个值";
console.log(testConstr.name) // 输出 --> 使用Object.defineProperty方法set了一个值
// 原型链
// 每个实例对象( object )都有一个私有属性(称之为 __proto__ )指向它的构造函数的原型对象(prototype )。
// 该原型对象也有一个自己的原型对象( __proto__ ) ,层层向上直到一个对象的原型对象为 null。
// 根据定义,null 没有原型,并作为这个原型链中的最后一个环节。
let fun = function () {
console.log(this) // fun(){}
this.a = 1;
this.b = 2;
}
/* 这么写也一样
function fun() {
this.a = 1;
this.b = 2;
}
*/
let ob = new fun();
console.log(ob) // {a: 1, b: 2}
// 在fun函数的原型上定义属性
// 不要在 fun 函数的原型上直接定义 fun.prototype = {b:3,c:4};这样会直接打破原型链
fun.prototype.b = 3;
fun.prototype.c = 4;
console.log(ob.a,ob.b,ob.c,ob.d); // 1 2 4 undefined
// 总结 (给对象设置属性会创建自有属性。获取和设置属性的唯一限制是内置 getter 或 setter 的属性)
// 整个原型链如下: {a:1, b:2} ---> {b:3, c:4} ---> Object.prototype---> null
// a是ob的自身属性值为 1
// b是ob的自身属性值为 2 原型上也有一个'b'属性,但是它不会被访问到,这种情况被称为"属性遮蔽"
// c不是ob的自身属性吗 ==> 那看看它的原型上有没有 c是ob.[[Prototype]]的属性值为 4
// d不是ob的自身属性 ==> 那看看它的原型上有没有 d不是 ob.[[Prototype]] 的属性 ==> 那看看它的原型上有没有 ob.[[Prototype]].[[Prototype]] 为 null,停止搜索 找不到d属性,返回undefined
// 当继承的函数被调用时,this 指向的是当前继承的对象,而不是继承的函数所在的原型对象
var foo = {
a: 2,
fb: function(){
return this.a + 1;
}
};
console.log(foo.fb()); // 3
// 当调用 foo.fb 时,'this' 指向了 foo.
var op = Object.create(foo); // op是一个继承自 foo 的对象
console.log(op,'op') // {} 'op'
op.a = 4; // 创建 op 的自身属性 'a'
console.log(op,'op') // {a: 4}
console.log(op.fb()); // 5
// 调用 op.fb 时,'this' 指向了 op 又因为 op 继承了 foo 的 fb 函数 所以,此时的 'this.a' 即 op.a,就是 op 的自身属性 'a'
// 嵌套函数和闭包
// 内部函数只可以在外部函数中访问。内部函数包含外部函数的作用域
// 内部函数形成了一个闭包:它可以访问外部函数的参数和变量,但是外部函数却不能使用它的参数和变量。
function outfun(a) {
function infun(b) {
return a + b;
}
return infun;
}
let fnIn = outfun(3); // 可以这样想:给一个函数,使它的值加3
res = fnIn(5);
console.log(res,'res') // 8 "res"
res1 = outfun(3)(5);
console.log(res1,'res1') // 8 "res1"
// 多层嵌套函数,B和C都形成了闭包,所以B可以访问A,C可以访问B和A;闭包可以包含多个作用域;他们递归式的包含了所有包含它的函数作用域。这个称之为作用域链
function A(a) {
function B(b) {
function C(c) {
console.log(a + b + c); // 输出6
}
C(3);
}
B(2);
}
A(1);
</script>
</body>
</html>