js基础知识深入大盘点
函数
都是Function的实例
每个js函数都是Function的对象实例,包括 箭头函数 和 对象方法
const fna = function () { } const fnb = () => { } const obj = { fnc() { } } console.log(fna.constructor===Function) // true console.log(fnb.constructor===Function) // true console.log(obj.fnc.constructor===Function) // true
length属性
每个js函数都有length属性,表示函数签名中“必传”的参数的个数。fn.length在定义js函数时就已经确定好了,任何时候都不变。
let fn = (a, b, c = 8, ...d)=> { } // fn.lenght 不会统计带有默认值的参数和剩余参数 console.log(fn.length) // 2
let fn = (a, b, c = 8, d)=> { } // fn.lenght 也不会统计 带有默认值参数后面的普通参数 // 剩余参数后面的普通参数?剩余参数只能写在最后! console.log(fn.length) // 2
arguments内置变量(箭头函数没有)
普通函数和对象方法,都有一个局部变量 arguments 描述实参。参考:arguments性能
function fn() { // arguments 只和实际传入的参数相关,与形参无关(这里形参个数为0) console.log(arguments.length) // 4 // arguments是一个可迭代对象,但不是数组(虽然类似于数组) for (let arg of arguments) { console.log('arg:', arg) // 分别输出:arg: 1,arg: 2,arg: 3,arg: 4 } } fn(1, 2, 3, 4)
function fn(...a) { // 即使形参( 即剩余参数:数组a)接收了所有的实参,arguments.length 依然是 4 // 箭头函数可以使用这样的剩余参数,代替arguments console.log(arguments.length) // 4 } fn(1, 2, 3, 4)
arguments只是类数组,可以这样转换成数组:
// 你可以这样将arguments转换为数组 var normalArray = Array.prototype.slice.call(arguments); // -- or -- var normalArray = [].slice.call(arguments); // -- or -- var normalArray = Array.from(arguments); // -- or -- var normalArray = [...arguments]; // -- or -- var normalArray = arguments.length === 1 ? [arguments[0]]: Array.apply(null, arguments) // 为什么要这样? // 当 Array()调用 只有一个参数且参数是数字时,会将其作为length,创建包含指定数量的空元素的数组; // 如果多于1个参数,就创建由这些参数组成的数组。 // 这看起来麻烦,但v8优化上更友好。 // 另外:new Array(1,2,3) 和 Array(1,2,3) 是一样的!( 有很多这样的例子。)
参数默认值
function fn(a=5) { console.log(arguments.length) // 1 console.log(a,arguments[0]) // 5 , undefined } fn(undefined)
function fn(a=5) { console.log(arguments.length) // 0 console.log(a,arguments[0]) // 5 , undefined } fn()
从上面的例子思考:如果你想真正没有传入参数时才采用默认值,即传入了undefined就使用undefined?
- 解构赋值默认参数
- 已经遇到的参数可以用作后面的默认参数
function f([x, y] = [1, 2], {z: z} = {z: 3}) { return x + y + z; } function greet(name, greeting, message = greeting + ' ' + name) { return [name, greeting, message]; }
- 每次函数调用时都会重新声明一次参数变量,并对默认参数重新赋值
- 调用一个函数分为两步:初始化参数(包含默认值初始化),然后才执行函数体。
// 示例一: function append(value, array = []) { array.push(value); return array; } append(1); //[1] append(2); //[2] // 示例二: let arr = [] function append(value, array = arr) { array.push(value); return array; } append(1); //[1] append(2); //[1,2],
剩余参数
剩余参数是真数组
解构剩余参数
function f(...[a, b, c]) { // 只取剩余参数中的前3个分别赋给a,b,c return a + b + c; }
箭头函数说明
- 箭头函数没有prototype属性。
-
yield
关键字通常不能在箭头函数中使用(除非是嵌套在允许使用的函数内)。因此,箭头函数不能用作函数生成器。 - 箭头函数也是函数也有自己的作用域,无论是let还是var变量
- 箭头函数不会创建自己的this,它只会从词法层面上的自己的作用域链的上一层继承this。
上层this变就跟着变!跟随所继承的this的变化而变化,当this从词法上指向了window(全局对象)理论上也是可以变的,但上层环境已无法变量,表现就是固定不变。 - 箭头函数的优先级:
callback = callback || function() {}; // ok callback = callback || () => {}; // SyntaxError: invalid arrow-function arguments callback = callback || (() => {}); // ok
Object内置方法
注意Object表示静态内置的函数对象,obj表示对象实例
Object.is(a,b) 比较相等 类似于a===b, 不过 :
=== 运算符将数字 -0
和 +0
视为相等 ,而将Number.NaN与NaN视为不相等.
Object.is将数字 -0
和 +0
视为不相等 ,而将Number.NaN 与NaN视为相等.
prototype相关
四个方法,其实影响的都是 __proto__ !!
Object.setPrototypeOf(obj, prot) // 设置obj的原型为prot对象(可以为null)。注意:设置的是__proto__
// 注意 console.log(obj.prototype===prot, obj.__proto__===prot, prot.isPrototypeOf(obj)) 结果分别为 false,true, true prototype1.isPrototypeOf(object1) // prototype1 是 object1 的原型(__proto__)吗? const object1 = Object.create(prototype1,config?) // 指定原型对象,创建一个对象. config 是属性配置,参考defineProperties Object.getPrototypeOf(object1) // 返回对象 object1 的原型对象
上述四个方法,设置原型,获取原型,判断原型等。注意特殊的 Object.create
let a = {} // 默认的原型是Object,所以a自带 toString 等方法 let a = Object.create(null) // 这时 a.toString() 就不存在了
当然Object.create也可以做简单的继承!
扩展,密封和冻结
对应了6个静态方法:
Object.seal(obj)
Object.freeze(obj)
Object.preventExtensions(obj)
Object.isSealed(obj)
Object.isFrozen(obj)
Object.isExtensible(obj)
不可扩展对象:不可添加新的属性而已, 但可以删除已有属性,可以配置已有属性,可赋值。
密封对象:对象不可扩展,配置不可改变 ,已有属性不可删除。但可重新赋值。
冻结对象:对象不可扩展,配置不可改变,已有属性不可删除,不可重新赋值。
属性定义相关
Object.defineProperties(obj,configs) // configs是 {key:config ...} 这样的对象 Object.defineProperty(obj,key,config) // get set value writable enumerable configurable Object.getOwnPropertyDescriptor(object1, 'property1'); // 返回config Object.getOwnPropertyDescriptors(obj) // 返回configs Object.getOwnPropertyNames(obj) // 返回的是所有 正常 的key 数字会变成字符串 Object.getOwnPropertySymbols(obj) // 返回的是所有 symbol 的key array1.propertyIsEnumerable(0) // 返回true,表示0这个属性时可枚举的 // 注意:for...in语句以任意顺序遍历一个对象的可枚举属性(除Symbol)。 obj.hasOwnProperty('key') // 对象obj,是否有key这个属性
注意:原型相关的 Object.create方法,也与属性相关!
其他常用
Object.assign(target,source1,source2...) // 改变target 并返回target Object.values(obj) // 返回values Object.keys(obj) // 返回keys Object.fromEntries(entries) // entries是一个二维数组,内部的每个数组都有两个元素[key,value] Object.entries(obj) // 返回entries数组
原型链
一张图了解原型链:
所有函数都是Function的实例:所以所有函数的__proto__最都指向 Function.prototype ,包括 Function.__proto__
所有对象都是Object的实例:所以所有对象的__proto__最终都指向 Object.prototype ,所有函数的prototype也是对象(包括Function的)自然也包括在内。
所有的函数都有两个隐含的对象(prototype和__proto__),所有对象都有一个隐含的对象(__proto__)。