JavaScript 语言精粹三(核心——函数)

时间:2021-1-8 作者:admin

JavaScript 语言精粹三

所有过失在未犯以前,都已定下应处的惩罚 – 威廉·莎士比亚《一报还一报》

起步:

  • JavaScript 中极具特色的设计就是其函数的实现了
  • 函数:即一组语句,它们是 JS 的基础模块单元,用于代码复用,信息隐藏和组合调用。
  • 编程:什么为编程?即把一组需求分解成一组函数和数据结构的技能。

函数对象

  • javascript中的函数就是对象,对象是‘键值对’的集合,并且隐形的连接到原型对象
    • 对象字面量产生的对象连接到 Object.prototype
    • 函数对象连接到 Function.prototype(此原型对象本身连接到 Object.prototype)
  • 函数对象在创建时,都配有一个prototype 属性[Foo.prototype],它的值是一个拥有constructor属性,且属性值为该函数的对象[Foo.prototype.constructor==Foo]
    • 书中的这句话有些拗口且不容易理解,所以我觉得用代码会直观一些
/**
 *  Foo() 构造函数
 * Foo.prototype 是构造函数Foo的原型/ 是poo的原型对象
 * poo.__prototype__ 也是指向原型对象
 */
function Foo() {} //构造函数
var poo = new Foo(); //实例化一个对象
console.log(poo.__proto__ === Foo.prototype); //true ,说明是指向同一个原型对象
console.log(poo.__proto__.constructor); //Foo()
console.log(Foo.prototype.constructor); //Foo()

函数字面量

  • 函数对象可通过函数字面量来创建,比如:
var add = function (par1, par2) {
  return par1 + par2;
};
  • 函数字面量分为四个部分
    • 第 1 部分是保留字function
    • 第 2 部分是函数名(可省略–匿名函数),函数可通过函数名递归调用自身
    • 第 3 部分是在调用(圆括号)中的一组参数,多个以逗号分隔。这些参数称之为函数中的变量
    • 第 4 部分是在执行(大括号)中的一组语句,它们是函数的主题,在函数调用时依次执行

注:函数字面量可出现在任何允许表达式执行的地方,函数可以定义在另一个函数中,并且函数除了可以访问自身的参数和变量外,还可以自由访问把它嵌套在自身函数体的父函数中的参数和变量这称之为闭包

  • 书中未举例,为便于理解我编写一个简单闭包函数:
/**
 * JS的特点就是函数内容可以读取全局/父级里变量
 * 函数外部无法读取函数内部定义的变量(内部声明要加let或var,否则默认声明的是全局变量)
 * 下方parent中的所有变量都是对getname可见的,反之则不成立
 */
function parent() {
  var name = "poo";
  function getname() {
    return name;
  }
  return getname;
}
var Aha = parent();
Aha(); //'poo'

函数的调用

  • 调用一个函数会暂停当前函数的执行,传递执行权和参数给新函数。
  • 除了声明时定义的形参,每个函数还另外接受两个特殊参数thisarguments
  • JavaScript中,一共有四种调用模式
    • 方法调用
    • 函数调用
    • 构造器调用
    • apply 调用
    • 这些模式在初始化关键参数this上存在很大的差异
  • 调用运算符是跟在函数表达式后的一对圆括号,括号内可包含零或多个表达式,每个表达式产生一个参数值
  • 实参与形参个数不匹配时不会出错,会忽略超出的参数值,会替换缺少的值为undefined

方法调用模式

  • 当一个函数被保存为对象的某一属性时,称它为一个方法,当该方法被调用时,this 就绑定到了该对象,若调用表达式包含 . 或者 [ ] 时,它被当做一个方法调用。
  • 例如:
var myObj = {
  val: 233,
  getVal: function (par) {
    this.val += typeof par === "number" ? par : 1;
  },
};
myObj.getVal();
console.log(myObj.val); //234
/*-------------------*/
myObj.getVal(433);
console.log(myObj.val); //666
  • 对象身上的方法可以通过 this 访问该对象身上的属性,因此也能进行取值和修改。
  • 通过 this 取得它所属对象的上下文的方法,称之为公共方法。

构造器调用模式

  • JavaScript是基于原型继承的语言,对象可直接从其他对象继承属性
  • 如果在一个函数前面加上 new 关键字调用,那么底层是会创建一个连接到该函数 prototype 成员的新对象,同时绑定 this 至新对象
  • 例如:
var Poo=function(city){
  this.citys=city;
}
Poo.prototype.getCity=function(){
  return this.citys;
}
var goWhere=new Poo('ZhengZhou');
console.log(goWhere.getCity());// ZhengZhou
  • 函数若通常使用 new 来结合调用,那么它就被称之为构造器函数
  • 约定俗成,这类函数要首字母大写用以区分

Apply 调用模式

  • apply 方法可以构建一个参数数组传递给调用函数,并且可自由选择 this 的指向
  • apply 中通常第一个参数为 this 绑定的值,第二个为参数数组

var add = function (par1, par2) {
  return par1 + par2;
};
var arrs=[1,2];
var count=add.apply(null,arrs) //3
//-构造一个包含type成员的对象,types并没有getRtx方法,但通过apply,仍可使用
var Poo=function(city){
  this.type=type;
}
Poo.prototype.getRtx=function(){
  return this.type;
}
var types={
  type:'RTX3080'
}
var getRtx=Poo.prototype.getRtx.apply(types);//RTX3080

参数

  • 当函数被调用时,都会拥有一个 arguments数组,它里面包含了所有被调用时,传递给它的参数
var count = function () {
  var i = 0;
  sum = 0;
  for (i; i < arguments.length; i++) {
    sum += arguments[i];
  }
  return sum;
};
console.log(count(1, 3, 4, 6, 7, 12)); //33
  • 因为设计上的失误,导致 arguments 并不是一个真正的数组,而是一个类数组对象(array-like)
  • arguments是一个拥有 length 属性的特殊对象,但它不具备任何数组方法
    • 数组对象的类型是 Arrayarguments的类型是 Object;
    • arguments 不能直接调用数组 API(不具备任何数组方法);
    • 数组遍历可以用 for in 和 for 循环,arguments 只能用 for 循环遍历;
//把函数中的arguments转为数组
var newArr = Array.prototype.slice.call(arguments);
console.log(Array.isArray(arguments)); //false
console.log(Array.isArray(newArr)); //true

返回

  • 当函数被调用时,从起始语句执行到遇到函数体 } 时结束,然后移交控制权至调用该函数的程序。
  • retrun 语句可使函数结束执行提前返回,若无指定返回值,则返回 undefined
  • js 中大部分函数在未设置 return 值时都会默认返回一个 underfined
  • getReturn 不加执行体()时,只是单纯的是一个指向函数的指针而已。
  • getReturn() 则是表示执行函数,并且获取函数执行的结果。
var getReturn = function () {
  return "hi~ Poo";
};
getReturn; //ƒ (){ return 'hi~ Poo'}
getReturn(); //hi~ Poo

扩充类型的功能

  • JavaScript 可以扩充语言的基本类型,比如避免每次添加方法时都写入 prototype 名称
Function.prototype.method = function (name, fun) {
  this.prototype[name] = fun;
  return this;
};
  • 可以通过给 Number.prototype 绑定自定义方法来简化我们的工作
  • 当参数为负时向上取整,为正时向下取整
Number.methods('getInteger',function(){
  return Math[this<0?'ceil':'floor'>](this);
})
console.log((5/3).getInteger())//1
  • 这种方法虽然方便,但是有很大的隐患,比如可能会覆盖类库中原有的方法
  • 所以上方的代码优化后如下:
Function.prototype.method = function (name, fun) {
  if (!this.prototype[name]) {
    this.prototype[name] = fun;
  }
  return this;
};

注意:通常避免混淆,可以利用 hasOwnProperty 来判断是否是自身属性(非继承)来的

  • 例如:
var Poo=function(){
  this.name='poo'
  this.getName=function(){
    console.log(`hi~ `${this.name})
  }
}
let aHa=new Poo()
console.log(aHa.hasOwnProperty('name')) //true
console.log(aHa.hasOwnProperty('getName')) //true
console.log(aHa.hasOwnProperty('toNumber'))//false

递归

未完,明日更新

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。