Function 的使用
// Function
const fun1 = new Function('a', 'b', 'c', 'console.log(a + b + c)');
const fun2 = new Function('a, b, c', 'console.log(a + b + c)');
fun1(1, 2, 3);
fun2(4, 5, 6);
特点
我们先从一个案例题目来看看,Function 的一个特点
var a = 1,
b = 2;
function test() {
var b = 3;
return new Function('c', 'console.log(a + b + c)');
}
var t = test();
t(4); // 7
从这个案例中,可以知道,最后 console.log
输出的值为:7。
一般来说,对于这个题目,有时候会傻傻分不清,输出的是7、还是8。
这里就涉及到一个 Function
的特点,Function
声明的函数,是全局作用域下的。针对于上题中的 new Function
,其实并不是声明在局部的 test
函数作用域下的,而是在全局作用域下的,所以对于 b
的取值,取的是全局作用域下的 2。
关于这点,可以查看 MDN Web 文档中 Function 介绍,它里面有这一段描述:
官方案例:
var x = 10;
function createFunction1() {
var x = 20;
return new Function('return x;'); // 这里的 x 指向最上面全局作用域内的 x
}
function createFunction2() {
var x = 20;
function f() {
return x; // 这里的 x 指向上方本地作用域内的 x
}
return f;
}
var f1 = createFunction1();
console.log(f1()); // 10
var f2 = createFunction2();
console.log(f2()); // 20
注意点:
new Function()
和Function
其实是一样的,是一个声明 + 定义一个函数,更重要的是不要看new Function()
有一个new
,其实它并没有返回一个对象,却返回了一个函数;- 例题中的
t(4)
和f1()
如果是在 Node.js 环境下执行的话,那会报错(找不到变量 a 或者 x 的 ReferenceError 错误),这是因为在 Node 环境下定义的var x
和var b
是在当前 Module 作用域下的,而不是全局作用于global
这个对象上的。
延伸
eval
阅读 MDN Web 文档后,文档中有说到由 Function
构造器创建的函数和使用 eval
执行创建函数是不同的,这里同样采用上面案例题目进行测试:
var a = 1,
b = 2;
function test() {
var b = 3;
// return new Function('c', 'console.log(a + b + c)');
eval('!function _ (c) { console.log(a + b + c) }(4)') // 输出 8
}
var t = test();
// t(4); // 7
在函数作用域中使用 eval
在函数声明的话,是存在于函数作用域中的,并且会形成闭包,由此这里可以验证 MDN 所描述的:由 Function
构造器创建的函数和使用 eval
执行创建函数是不同的。
匿名函数自执行
从上面延伸知识点的 eval
节可以看到,匿名函数自执行的时候,在前面加了一个 !
,这是什么意思呢?
其实这是告诉解析器,这是一段函数表达式,并且也只是匿名函数自执行的其中一个方法。
+function () { console.log("self 1"); }();
!function () { console.log("self 2"); }();
(function() { console.log("self 3"); })();
(function() { console.log("self 4"); }());
[function() { console.log("self 5"); }()];
以上几种方法都是匿名函数自执行的方法。
考题学知识
以下是一道面试题,能够全面认识到 function 的相关知识
// func1
function Foo() {
getName = function () {
console.log(1);
}
console.log(this);
return this;
}
// func2
Foo.getName = function () {
console.log(2);
}
// func3
Foo.prototype.getName = function () {
console.log(3)
}
// func4
var getName = function () {
console.log(4);
}
// func5
function getName() {
console.log(5)
}
// print 1
Foo.getName() // 2
// print 2
getName() // 4
// print 3
Foo().getName() // 1
// print 4
getName() // 1
// print 5
new Foo.getName() // 2
// print 6
new Foo().getName() // 3
print 7
new new Foo().getName() // 3
// print 8
new new Foo() // error,Uncaught TypeError: (intermediate value) is not a constructor
看到上面执行的输出是不是有一些感到困惑,现在来分析一下,整个流程是怎么跑的。
我们先来看看所有函数的声明和定义:
-
首先代码在执行的时候会进行一个预编译处理,而这个预编译的过程,按照从上到下声明一个
Foo
函数,也就是 func1,接下来会声明getName
,也就是 func5 -
预编译完成后,游览器执行这段代码,func2 会给
Foo
定义一个静态属性,func3 会给Foo
定义一个原型属性,func4 执行的时候,会对全局 window 属性进行重新赋值,也就是上面第一点中的getName
在这个时候会进行重新赋值
到此函数的声明和定义就都完成了,在这里可以发现,func5 已经被覆盖掉了,将不会有进行调用它的机会了,下面我们来看看输出结果:
// 这里执行的就是 Foo 的静态方法 fun2
Foo.getName() // 2
// 这里执行的是 func5,这里的 getName 的演变从 func5 -> func4
getName() // 4
// 这里首先执行的是 Foo,Foo 执行的时候,内部的 this 为全局 winsow 对象
// Foo 中会进行 getName 重新赋值,所以 getName 的演变从 func5 -> func4 -> func1 中的 getName
// 所以 Foo() 执行后返回 this -> window 然后执行 func1 中的 getName
Foo().getName() // 1
// 这里就是执行全局的 getName,他的演变过程为 func5 -> func4 -> func1 中的 getName
getName() // 1
// 这里就是执行 Foo 的静态属性 func2,只不过这个 func2 作为 new 的构造函数了一下
new Foo.getName() // 2
// 首先 Foo 作为构造函数 new 了一个实例,这时候 Foo 返回的 this 指的就是这个 new 出来的实例,然后使用该实例 this 调用 getName 就会执行 func3 的原型方法
new Foo().getName() // 3
// Foo 作为构造函数 new 了一个实例, 然后返回的实例 this 执行了原型方法 getName 并且作为第一个 new 的构造函数
new new Foo().getName() // 3
// Foo 作为构造函数 new 了一个实例,然后这个返回的的实例对象作为第一个 new 的构造函数,这时候就会报错了,说了 new 的时候这个中间值不是一个构造函数
new new Foo() // error,Uncaught TypeError: (intermediate value) is not a constructor
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!