1、JS中“万物皆对象”,所以Funtion() 也是一种对象(对象角色属性),只不过是一种内置的特殊对象;
2、Funtion()对象是通过什么方式创建的呢?通过自身创建自身,Funtion()可以说是js中万物的起源,不要从原型链的角度找源头,要从“构造函数链”找源头可能更容易理解原型链、数据类型之间的关系。
所有的对象都是通过“new 构造函数()"的创建的,所以“Funtion( )对象”也是通过“new Funtion( )"(函数角色属性,不传参数)方式创建的,即Funtion( ) ===new Funtion( )为true;
3、内置的构造函数(Object()、Array()、String()等)是如何创建的?内置的构造函数也是对象,也可以通过“new Funtion('逻辑代码')"(传参数)来创建。
可以将需要创建的内置构造函数的逻辑代码以参数的形式传递给“new Funtion('逻辑代码')",只不过这些逻辑代码浏览器进行了内置封存,开发者无法访问,只会以function(){native code}形式显示;
4、有了内置构造函数,就可以通过“new 内置构造函数()"方式创建内置类型的对象;
5、有时内置类型的对象无法满足需求,需要我们自己创建一些其他类型的自定义类型对象,那怎么创建自定义类型对象呢?和内置类型对象的创建方式类似,只是自定义构造函数的逻辑代码可以查看。
可以将需要创建的自定义构造函数的逻辑代码以参数的形式传递给“new Funtion('逻辑代码')"来创建自定义构造函数,通过“new 自定义构造函数()"方式创建自定义类型的对象;
6、每个 JavaScript 函数(包括字面量函数-字面量函数内部实现也是用的new Function()来实现的、new Function()创建的函数)实际上都是一个 Function 对象。运行 (function(){}).constructor === Function // true 便可以得到这个结论。注意:函数类型只有一种,没有自定义函数类型,只有自定义对象类型,所以想要想要创建函数只能通过“new Function("代码参数")”方式创建,不能通过“new 自定义构造函数()”方式创建;“new 自定义构造函数()”只能创建对象,不能创建函数,可以通过Object.prototype.toString.call() 检测。
注意:
函数具有两种数据类型角色的属性:这是一个容易迷惑的问题点。
1.函数数据类型:在创建对象时,充当构造函数的角色;
2.对象数据类型:通过“new Funtion('代码参数')"创建出来的函数对象(一种特殊的内置类型对象),充当函数对象的角色;
上面的6点内容结合这张图更容易理解。
补充内容:
构造函数通常以首字母大写的方式命名,如果首字母不大写的函数,我们称之为普通函数。
普通函数和构造函数本质上并没有并没有什么区别,只不过是使用场景不同而已;普通函数是用来分装功能代码逻辑的,通过正常调用封装的代码逻辑函数实现功能需求;构造函数用于创建生成对象,只要构造函数的代码逻辑适合生成特定功能的对象,就可以通过“new 构造函数('代码参数')"的方式创建生成对象。
当然我们也可以随便用一个普通函数来创建对象:let obj= new 普通函数('代码参数'),obj 的数据类型也是对象,只是可能这个对象包含的内容功能未必能符合自己的需求,所以在创建调用构造函数时要符合new关键字的规则。
“new Funtion('代码参数')"创建函数的方式存在的缺点:
这种“new Funtion('代码参数')"创建函数的方式是一种原始的创建方式,这种创建方式有些缺点,为了能够方便开发人员、可读性更强、开发效率更高,js规范还允许一种字面量方式创建函数(字面量函数内部实现也是用的new Function()来实现的)。这两种方式创建的函数从功能和技术实现上来说几乎差不多,只是有一些不同点:
1、“new Funtion('代码参数')"创建的函数只有全局作用域和自己的局部变量,也就是说:无论在哪里创建、调用这种方式创建的函数,它都始终只在全局作用域中运行;而字面量函数是拥有全局作用域和局部作用域的;同时也意味着无法创建当前所在函数作用域的闭包。
2.和evel()一样,“new Funtion('代码参数')"创建的函数存在安全和性能方面的问题。
由于以上几方面的缺点,所以在实际的开发中不推荐使用“new Funtion('代码参数')"创建函数,而是使用字面量函数。
内置构造函数Funtion( )的原型上具有的一些属性和方法,所有的函数也都将从Function()原型上继承这些属性和方法:
属性:
Function.prototype.name:函数名称,默认不可修改(类似身份证上的姓名);匿名函数的name值为:anonymous、bind()绑定函数的name值为:bound 函数名、访问器函数的name值为:get/set 函数名;
Function.prototype.displayName:函数别名,可修改(类似人的小明、昵称、艺名);
Function.prototype.length:形参的个数,也就是函数希望接受的参数个数,不包括剩余参数个数,只包括第一个默认值参数的之前的参数;arguments.lenth是实际传入参数的个数、只能在函数内部调用。
方法:
Function.prototype.call(thisArg,item1,item2,...)和Function.prototype.apply(thisArg,arrayArg):在调用函数时使用,都是用于改变函数的this指向,只是传递给调用函数的参数的方式不同而已。这两个方法在借用已有方法十分的高效:Math.min.apply(null,[2,1,5,4])、const array = ['a', 'b'];const elements = [0, 1, 2];array.push.apply(array, elements)、var slice = Array.prototype.slice;slice.apply(arguments)等;
Function.prototype.bind(thisArg,item1,item2,...):在创建函数的使用,以后调用的时候就默认绑定了,不需要重复绑定了。如果使用new运算符构造绑定函数,则忽略thisArg该值。
以上三种方法,如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。
返回值为:调用有指定 this 值和参数的函数的结果。
Function.prototype.toSting():获取函数的函数体代码的字符串形式,和不带函数名的函数是一样的输出,都是函数体字符串。此方法是从Object.prototype.toSting()继承而来的,但是Function.prototype.toSting()又重写此方法。
Function.prototype上的四种方法的实现方式:待补充!