JavaScript高级第2天:定义函数的三种方式、函数的原型链结构、完整原型链、作用域以及作用域链、函数的四种调用模式、闭包、计数器、斐波那契数列优化、三种继承方式
JavaScript高级第二天
01-定义函数的三种方式
1.函数声明 function:可以先调用再声明,因为预解析(把函数声明、变量声明进行提升)
function fn() {//函数体conle.log(1);
}
2.函数表达式:不可以先调用再声明,因为预解析只是把变量声明提升了,赋值留在原位。
var fn2 = function() {conle.log(2);
}
fn(2);
3.函数也是对象,函数也是通过构造函数new出来的 Function(大写的)(了解,工作当中不会使用到)
语法:
var fn3 = new Function(arg1, arg2, arg3..., bodyFn);
参数:
1.参数的个数是若干个
2.参数的类型都是字符串类型的
3.除了最后一个参数bodyFn外,其他参数都是创建的函数的形参
4.最后一个参数bodyFn是创建的函数的函数体
例:
var fn4 = new Function('n1', 'n2', 'alert(n1 + n2)');// 等价于以下代码:
// var fn4 = function(n1, n2) {// alert(n1 + n2);
// }
02-函数的原型链
function Person(){}
// 底层是通过 Function new 出来的
// var Person = new Function(); //Person是实例对象,是函数
Person的原型链:
Person ==> Function.prototype ==> Object.prototype ==> null
Function.prototype常用成员
- call:调用函数,重新指向this
- apply:调用函数,重新指向this
- bind:重新指向this,返回一个新的函数,不调用。
03-完整版原型链
核心的点:函数的双重身份,函数是函数,也是对象
1.函数作为构造函数来使用去创造对象
function Person(){}
var p = new Person();
绘制原型三角关系:
- 构造函数:Person
- 原型对象:Person.prototype
- 实例对象:p
2.把函数当实例看,函数也是构造函数 Function 创建
底层就是 var Person = new Function();
绘制原型三角关系:
- 构造函数:Function
- 原型对象:Function.prototype
- 实例对象:Person
3.把Object考虑进来 ==> 当构造函数来使用
var obj = new Object();
绘制原型三角关系:
- 构造函数:Object
- 原型对象:Object.prototype
- 实例对象:obj
4.把Object当成实例对象来使用
任何函数底层都是 Function 创建的,都是Function的实例对象
底层 var Object = new Function();
绘制原型三角关系:
- 构造函数:Function
- 原型对象:Function.prototype
- 实例对象:Object
5.Function 当实例对象来看,谁把 Function 创建出来的
var Function = new Function();
绘制原型三角关系:
- 构造函数:Function
- 原型对象:Function.prototype
- 实例对象:Function
绘制完整版原型链的目的是辅助大家理解js中对象的继承关系:
小结:
任何对象的原型链上都有Object.prototype
任何函数的原型链上都有 Function.prototype
函数是一等公民,
- typeof 函数==> function
- Function.prototype ==> 原型对象的类型是个函数
- Function 自己生自己
函数的双重身份
是函数,也是对象
是函数,有prototype属性
是对象,有 _ _ proto_ _ 属性
所以说函数有prototype属性,也有 _ _ proto_ _ 属性
练习题1:(看图就可以完全答出来)
Function.prototype === Function.prototype true
Object.__proto__ === Function.prototype true
Function.prototype.__proto__ === Object.prototype true
Object.prototype.__proto__ === Object.prototype false
Object.__proto__.__proto__ === Object.prototype true
04-练习题
function Person() {}
var p = new Person()console.log(p.constructor) // Person
console.log(Person.constructor) // function
05-instanceof
instanceof:实例对象
语法:
对象 instanceof 构造函数
作用:
- 字面上理解:对象是否是构造函数的实例对象,如果是的,就返回 true
- 从原型链角度来理解:
- 构造函数的prototype属性是否在对象的原型链上,如果在 返回true,否则返回false
练习题
06-作用域
- 变量可以起作用的区域(就是说一个变量声明定义好了,就可以在哪些区域内使用)
- 在js中,有全局作用域和函数作用域(词法作用域,静态作用域)
函数作用域:当函数声明定义好了,其函数作用域就定下来了,其作用域链也定下来了
作用域链:任何函数可以形成作用域,函数嵌套在另外一个函数中,那么外层函数也有自己的作用域,这样从里到外直到全局作用域,形成的链式结构叫做作用域链。
每一个函数的作用域链:都是从当前函数作用域出发,从里到外形成
07-变量的搜索原则
沿着作用域链来查找
- 首先会在当前作用域内查找是否有声明该变量,如果有就返回变量的值
- 如果没有,就去外层作用域中查找
- 如果外面也没有,就沿着作用域链继续往外找,直到全局作用域,如果有就返回
- 如果还没有,报错。(属性值找不到就是undefined,变量找不到就报错)
08-练习题
09-函数的四种调用模式
分析this指向的问题:
- 任何函数都有属于自己的this指向。
- this指向是动态灵活的,只有当函数调用的时候,才能确定下来this的指向
- 如何去分析this指向:
- 看这个this属于哪个函数
- 看这个函数是如何调用的 ==> 就能决定下来this指向谁
函数的四种调用模式:
第一种:函数调用模式
这种调用模式,函数内的this指向window。
第二种:构造函数调用
new + 构造函数():这种调用模式,构造函数的this指向新创建出来的实例对象
第三种:方法调用模式
对象.方法名():这种调用模式,方法内的this指向调用方法的那个对象
谁调用,this指向谁
是点语法和中括号语法,都是方法调用模式
练习题:
function foo() {conle.log(this); //arr
}
var arr = [10, 20, foo];
// 方法调用模式
arr[2]();
第四种:上下文调用模式 ==> call()、apply()、bind() 这三个方法
10-练习题
// 1.
var age = 38;
var obj = {age: 18,getAge: function () {console.log(this.age);}
}var f = obj.getAge; //仅仅是把getAge方法赋值给了变量f
// 函数调用模式,函数内的this,this指向window
f();//38// 2.
var age = 38;
var obj = {age:18,getAge:function () {// 方法内的this指向objconsole.log(this.age);//18function foo(){// 任何函数都有属于自己的this// foo内的this和getAge方法内的this不是一个,没有关系console.log(this.age);//38}// 函数调用模式foo();}
}
// 方法调用模式,
obj.getAge();
obj["getAge"]();// 3.
var length = 10function fn() {console.log(this.length) // 10 3
}var obj = {length: 5,method: function (fn) {// 函数调用模式 fn函数内的this指向windowfn() // 10// arguments 伪数组:实参列表 ==> 类似于这样[fn, 10, 5]// 方法调用模式,fn这个方法被arguments调用arguments[0]();}
}
obj.method(fn, 10, 5);
几种特殊的this指向
- 定时器中的this指向了window,因为定时器的function最终是由window来调用的。
- 事件中的this指向的是当前的元素,在事件触发的时候,浏览器让当前元素调用了function
11-上下文调用模式
作用:可以自己去取指定this指向
call()、apply()、bind() 三个方法,任何函数都可以去使用以上三个方法
1. call()方法:可以用来调用函数
1.除了小括号可以调用函数外,还可以使用call()调用函数
演示:
function fn() {conle.log(111);
}
fn();
fn.call();
2.call的第一个参数可以用来修改函数内的this指向
function fn(2) {conle.log(2);conle.log(this);
}
fn2(); // 2 window
fn.call([10, 20, 30]); // 2 [10, 20, 30]
3.call的参数是若干个
第一个参数 ==> 用来修改this指向
除了第一个参数之外的所有其他参数 ==> 都是用来给函数传递实参的
function fn3(n1, n2) {conle.log(this);conle.log(n1 + n2);
}
// fn3(10, 20)
fn3.call([], 100, 200); //
作用:
- 调用函数
- 第一个参数来改this指向
- 给函数传递实参 ==> 除第一个以外的所有参数
12-方法借用(调)模式
上下文调用模式又称方法借用(调)模式
13-伪数组与数组
伪数组与数组最大的区别:伪数组不能使用数组的方法,但可以让伪数组去借用数组的方法
伪数组定义:是个对象,有数字下标还有length属性,所以可以和数组一样进行循环,但是伪数组不能使用数组的方法。
常见的伪数组:
- arguments实参列表
- querySelectorAll()、getElementByTagName() 获取到的元素
- jq对象
实例:
// 需求:给obj伪数组添加 2: '波哥'
var obj = {0: '班班',1: '大飞哥',length: 2
}// 这是常规做法(第一种方法)
// obj[2] = '波哥';
// 还需要手动维护length
// obj.length++;
// conle.log(obj);// Array.prototype.push:是从数组中的原型对象中去拿到push// [].push ==> 从空数组上去获取到push方法// 第二种:
// 数组的原型对象:
// Array.prototype.push.call(obj, '波哥'); //效果等价于:obj.push('波哥')// 第三种:
;[].push.call(obj, '波哥', '啦啦', 'kuqi');conle.log(obj);
注意点:
[]。push 简化写法,一定要注意: {} 和 [] 一定要使用
;
分号隔开,否则语法报错数组的push方法不仅有添加功能,内部还会自动的去维护length属性
14-伪数组借用数组的方法
var obj = {0: '班班',1: '大飞哥',2: '哥哥'length: 3
};
把obj伪数组里面的每一项拼接成字符串 '班班-大飞哥-哥哥'
var str = [].join.call(obj, '-');
console.log(str);
15-apply方法
call方法和apply方法作用是一样的,写法上有区别
一样的参数:
- 可以调用函数
- 可以修改this指向
- 可以给函数传递实参
apply的语法
apply(thisArg, [实参列表])
- thisArg用来修改函数内的this指向
- 实参列表:是个数组或者伪数组,里面的就是传递给函数的所有实参
演示:
function fn() {console.log(1);
}
fn();
fn.call();
fn.apply();function fn2() {console.log(2);console.log(this);
}
fn2.call({name: 'call'});
fn2.apply({name: 'apply'});function fn3(n1, n2) {console.log(this);console.log(n1 + n2);consloe.log(arguments);
}
fn3.call([], 1, 2);
fn3.apply([10, 20], [30, 50]);
apply的平铺性:apply会把第二个参数里面的每一项取出来作为实参给函数传递过去。
闭包
闭包的基本概念
闭包(closure)是JavaScript语言的一个难点,也是JavaScript的一个特色,很多高级的应用都要依靠闭包来实现。
闭包的概念
在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数
在JavaScript中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则产生闭包。
产生闭包的条件
当内部函数访问了外部函数的变量的时候,就会形成闭包。
闭包的作用
保护私有变量不被修改
计数器
需求:统计一个函数的调用次数
var count = 0;function fn(){count++;console.log("我被调用了,调用次数是"+count);}fn();fn();fn();
缺点:count是全局变量,不安全。
使用闭包解决这个问题!!!!
function outer(){var count = 0; // 私有变量, 将count保护起来了function add(){count++;console.log("当前count"+count);}return add;}var result = outer();result();
斐波那契数列优化
缓存(cache):数据的缓冲区,当要读取数据时,先从缓冲中获取数据,如果找到了,直接获取,如果找不到,重新去请求数据。
计算斐波那契数列,会有很大的性能问题,因为重复的计算了很多次,因此我们可以使用缓存来解决这个性能问题。
初级优化:
使用缓存的基本步骤:
- 如果要获取数据,先查询缓存,如果有就直接使用
- 如果没有,就进行计算,并且将计算后的结果放到缓存中,方便下次使用。
//缓存var arr = [];var fbi = function (n) {count++;if (n == 1 || n == 2) {return 1;}if (arr[n]) {return arr[n];} else {var temp = fbi(n - 1) + fbi(n - 2);arr[n] = temp;//存入缓存return temp;}}
缺点:既然使用缓存,就需要保证缓存的数据的安全,不能被别人修改,因此,需要使用闭包来实现缓存的私有化。
function outer() {//缓存var arr = [];var fbi = function (n) {if (n == 1 || n == 2) {return 1;}if (arr[n]) {return arr[n];} else {var temp = fbi(n - 1) + fbi(n - 2);arr[n] = temp;//存入缓存return temp;}}return fbi;}var fbi = outer();console.log(fbi(40));
闭包经典面试题
打印下标
继承
现实生活中的继承,子承父业, 如儿子继承了父辈的财产,公司等
程序中的继承,一个对象可以使用另一个对象中的方法或属性
继承的目的: 方便代码的复用
var lw = {skill: "翻墙",age: 28}function Person(){}var xm = new Person();// 如何实现让xm可以使用到lw对象上的skill属性???xm.skill; // ==> 翻墙
JS常见的几种继承模式
原型链继承
一个对象可以访问构造函数的原型中的属性和方法,那么如果想要让一个对象增加某些属性和方法,只需要把这些属性和方法放到原型对象中即可。这样就实现了继承, 称之为原型链继承
- 直接给原型增加属性和方法
- 原型替换(注意:constructor)
借用构造函数继承
从构造函数中继承到构造函数中的成员
function Person(name, age, gender){this.name = name;this.age = age;this.gender = gender;}function Chinese(name, age, gender, skin){this.name = name;this.age = age;this.gender = gender;// 以上代码重复,在构造函数Person中已经给this添加this.skin = skin;}// 重写构造函数Chinesefunction Chinese(name, age, gender, skin){// 借用Person构造函数,继承到name、age、gender属性Person.call(this, name, age, gender);this.skin = skin;}
组合继承
借用构造函数 + 原型链继承组合在一起使用
function Person(name, age, gender){this.name = name;this.age = age;this.gender = gender;}// Person原型上的sayHi方法Person.prototype.sayHi = function(){console.log("hello, 我是" + this.name);}function Chinese(name, age, gender, skin){// 借用Person构造函数,继承到name、age、gender属性Person.call(this, name, age, gender);this.skin = skin;}var xm = new Chinese("xm", 20, "male", "黄色");// xm有name/age/gender属性从Person构造函数中继承到的// 那么如何让Chinese的实例对象xm去继承到Person原型上的sayHi方法???xm.sayHi();
JavaScript高级第2天:定义函数的三种方式、函数的原型链结构、完整原型链、作用域以及作用域链、函数的四种调用模式、闭包、计数器、斐波那契数列优化、三种继承方式相关推荐
- 斐波拉契数列 Java三种实现
对于很多Java初学者来说,求解斐波拉契数列,基本上所使用的方式都是递归.对于求解斐波拉契数列来说,递归的实现方式的效率是极其低下的. 在此,我贴出三种实现(递归,迭代,动态规划): 我们先看代码的执 ...
- “斐波那契数列”的两种算法
"斐波那契数列"的两种算法 斐波那契数列有个规律:从第三个数开始,每个数是前两个数之和,比如: 1 1 2 3 5 8 13 21 34 55...... 现在通过两种方式(递归与 ...
- php猴子吃桃子问题n天,『PHP学习笔记』系列四:利用函数递归调用思想解决【斐波那契数列】问题和【猴子吃桃问题】问题...
什么是函数递归思想? 递归思想:把一个相对复杂的问题,转化为一个与原问题相似的,且规模较小的问题来求解. 递归方法只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量. 但在 ...
- C语言--斐波那契数列(三种方法)
文章目录 一·介绍 二·代码实现 1·递归实现 2·迭代实现 3·数组实现 一·介绍 斐波那契数列,就是前两个数是1,之后从第三个数开始等于前面两个数的和,请用代码方式求出第n个斐波那契数列的大小. ...
- 斐波拉契数列的三种实现方法
百度解释 斐波那契数列(Fibonacci sequence),又称黄金分割数列.因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为"兔子 ...
- 斐波拉契数列的三种解法
斐波那契数列: f(n)=f(n-1)+f(n-2); n>=2 f(0)=0; f(1)=1; 即有名的兔子繁衍问题. 斐波那契数列共有三种解法,因而写这篇文章总结一下. 1. 递归求解 递归 ...
- php算法求出兔子数列,PHP算法:斐波那契数列的N种算法
前言 前段时间,遇到优化计算斐波那契数列的常规递归方法,但是一时间并没有及时想到很好的方法,所以后面查找了相关资料,总结了多种计算解法,所以分享出来,和大家一起交流学习. 斐波那契数是什么 斐波那契数 ...
- 斐波纳契数列 java_几种复杂度的斐波那契数列的Java实现
一:斐波那契数列问题的起源 13世纪初期,意大利数论家Leonardo Fibonacci在他的著作Liber Abaci中提出了兔子的繁殖问题: 如果一开始有一对刚出生的兔子,兔子的长大需要一个月, ...
- C++输出斐波那契数列的几种方法
定义: 斐波那契数列指的是这样一个数列:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ... 这个数列从第三项开始,每一项都等于前两项之和. 以输出斐波那 ...
最新文章
- python 设计模式 观察者_python设计模式之观察者模式
- Apache TinkerPop毕业成为顶级项目
- python webdriver点击指令_测开系列Selenium Webdriver Python(20)--Webdriver运行原理
- 《大话数据结构》读书笔记-图
- windows10安装mysql 8.0_手把手教你在Windows 10安装MySQL 8.0(详细图文)
- Http请求处理流程
- iOS学习笔记15-设计模式之 适配器模式
- origin2016中怎么画多条曲线,并且分别给不同曲线设置标记
- JAVA程序员基本功:开发实现类隐藏及应用
- android贪吃蛇设计报告,基于android的贪吃蛇游戏设计与开发
- JAVA文件传输原理及介绍—狂神说
- 建筑施工企业工程项目成本管理与控制对策
- html5时钟在图片上画指针,HTML5使用canvas元素绘制指针式动画时钟_网页代码站(www.webdm.cn)...
- 服装尺寸 html,国家标准服装尺寸表
- 只有7步,就能将任何魔方6面还原
- 5g cpe参数及功能介绍
- oCPC实践录 | 广告冷启动问题的思考与总结
- php 打开word显示无法打开文件,word无法打开文件,因为内容有误怎么办
- C#中WorkBook的操作
- Android中当item数量超过一定大小时,将RecyclerView高度固定