JavaScript進階
變量類型
tyoeof:
typeof undefined //undefined
typeof 'abc' //string
typeof 123 //number
typeof true //boolean
typeof {} //object
typeof [] //object
typeof null //object
typeof console.log //function
==:
100='100' //true
0 == '' //true
null == undefined //true
使用==||===:
推薦:出了下面這個其他地方全部用三等
if(obj.a==null){//相當於obj.a===null || obj===undefined這個的簡寫形式
}
內置函數有:
Object
Array
Boolean
Number
String
Function
Date
RegExp
Error
Math
…
創建對象:
- 創建一個新對象
- this指向新對象
- 執行代碼,賦值等運算
- 返回this
- 如果構造函數返回一個對象,那麼這個對象會取代整個new出來的結果。那麼這個對象會取代整個new出來的結果。如果構造函數沒有返回對象,那麼new出來的結果爲步驟1創建的對象
1、字面量創建
全爲靜態成員,適用於tools類等
var MyMath = {
PI: 3.1415926,
max: function () { },
min: function () { }
}
console.log(MyMath.PI);
2、工廠方法創建對象
創建對象爲Object的對象,而不是一個hero的對象
// 工廠函數創建對象
function createHero(name, blood, weapon) {
var o = new Object();
o.name = name;
o.blood = blood;
o.weapon = weapon;
o.attack = function () {
console.log(this.weapon+'攻擊敵人');
}
return o;
}
var hero = createHero('劉備',100,'劍');
3、構造函數
全爲實例成員,創建的對象爲hero,但多個對象會存儲多個同樣的attack方法,佔用代碼冗餘
function Hero(name, blood, weapon) {
this.name = name;
this.blood = blood;
this.weapon = weapon;
this.attack = function () {
console.log(this.weapon+'攻擊敵人');
}
}
var hero =new Hero('劉備',100,'劍');
改1:
解決了存儲問題,但污染了全局作用域
function Hero(name, blood, weapon) {
this.name = name;
this.blood = blood;
this.weapon = weapon;
this.attack = attack;
}
function attack() {
console.log(this.weapon + '攻擊敵人');
}
var hero = new Hero('劉備', 100, '劍');
4、使用原型
解決了存儲問題,同樣不污染全局作用域
function Hero(name, blood, weapon) {
this.name = name;
this.blood = blood;
this.weapon = weapon;
}
Hero.prototype.attack = function () {
console.log(this.weapon + '攻擊敵人');
}
var hero = new Hero('劉備', 100, '劍');
簡單原型
在 JavaScript 中,每當定義一個對象(函數也是對象)時候,對象中都會包含一些預定義的屬性。其中每個函數對象都有一個prototype
屬性,這個屬性指向函數的原型對象。原型對象,顧名思義,它就是一個普通對象。
原型鏈
繼承
原型繼承:
無法設置構造函數的參數
function Person() {
this.name = 'zs';
this.age = '18';
this.sex = '男'
}
function Student() {
this.score = '99';
}
Student.prototype = new Person();
Student.prototype.constructor = Student;
function Teacher() {
this.salary = 3000;
}
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-JBpBys6Y-1570539608298)(C:\Users\10592\AppData\Roaming\Typora\typora-user-images\1564197765564.png)]
借用構造函數
方法怎麼繼承呢?
// 借用構造函數
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
function Student(name, age, sex,score) {
Person.call(this,name,age,sex);
this.score = score;
}
組合繼承:借用構造函數+原型繼承
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
Person.prototype.sayHi = function () {
console.log('大家好!我是' + this.name);
}
function Student(name, age, sex, score) {
Person.call(this, name, age, sex);
this.score = score;
}
// 讓字類型繼承父類型的方法
Student.prototype = new Person();
Student.prototype.constructor = Student;
Student.prototype.exam = function () {
console.log('考試');
}
function Student(name, age, sex, score) {
Person.call(this, name, age, sex);
this.score = score;
}
Teacher.prototype = new Teacher();
Teacher.prototype.constructor = Teacher;
Teacher.prototype.gaijuan = function () {
console.log('改卷');
}
函數
函數的定義方式
函數聲明與函數表達式
// 函數的聲明
fn();
function fn() {
console.log('test');
}
//報錯
// 函數表達式
fn1();
var fn1= function () {
console.log('test');
}
區別:
- 上面代碼由於聲明提前可看成:所以
fn1()
處報錯
//聲明提前
function fn() {
console.log('test');
}
var fn1;
fn();
fn1();
fn1= function () {
console.log('test');
}
- 在老版本的IE中
if
語句中的函數聲明會提前 但函數表達式並不會發生這樣的情況
// 瀏覽器問題
// 根據條件聲明函數
// 在現代瀏覽器 不會提升if語句中的函數聲明
// 在老版本的IE中 if語句中的函數聲明會提前
if(true){
function fn() {
console.log('test--true');
}
}else{
function fn() {
console.log('test--false');
}
}
運行結果:
現代瀏覽器:test--ture
IE8:test--false
new Function
不推薦使用,因爲執行速度慢(先解析字符串,在執行)
認識到函數也是對象
var fn = new Function('var name = "張山";console.log(name);')
fn();
console.dir(fn);
this
函數內部的this,是由函數調用的時候來確定其指向
普通函數調用
this指向window
// 1、普通函數
function fn() {}
fn();
//相當於window.fn();
方法調用
this指向obj,指向調用該方法的對象
// 2、方法調用 this指向obj
var obj={
fn:function () {}
}
obj.fn();
構造函數調用
構造函數內部的this指向由構造函數創建的對象,this指向hero實例
// 3、構造函數調用 構造函數內部的this指向由構造函數創建的對象
function Hero(name, blood, weapon) {
this.name = name;
this.blood = blood;
this.weapon = weapon;
}
Hero.prototype.attack = function () {
console.log(this.weapon + '攻擊敵人');
}
var hero = new Hero('劉備', 100, '劍');
hero.attack();
作爲事件處理函數調用
作爲事件處理函數調用 this指向觸發該對象的對象,this指向btn
// 4、作爲事件處理函數調用 指向觸發該對象的對象
btn.onclick=function(){
console.log(this);
}
作爲定時器的參數
作爲定時器的參數 this指向window
// 5、作爲定時器的參數 this指向window
setInterval(function() {
console.log(this);
}, 1000);
函數中的方法
call的應用
- 使僞數組使用數組的方法
- 使
arr
數組可以使用Object
的toString
而不是自己重寫的toString
可以找到數組對象的類型
// 僞數組
var obj={
0: 100,
1: 60,
2: 60,
length: 3
};
Array.prototype.push.call(obj,30);
Array.prototype.splice.call(obj,0,2);
console.dir(obj);
var arr = [5,9];
console.log(arr.toString());
console.log(Object.prototype.toString.call(arr));
最後三句輸出:
- 5 ,9
- object Array
apply的應用
可以把數組中的每一項展開
// apply
var arrp = [6,10,5,9,3];
console.log(Math.max.apply(Math,arr)); //輸出:10
console.log(null,arr); //輸出:6 10 5 9 3
bind的應用
-
改變定時器的this指向
-
改變事件處理函數中的this指向
改變定時器的this指向,讓this爲function中的this,而function的又是obj調用,所以this指向obj
var obj={
name:'zs',
fun:function () {
setInterval(function() {
console.log(this.name);
}.bind(this), interval); //這裏的this是function的this,
}
}
obj.fn();//輸出zs
btn.onclick = function () {
}.bind(obj);
其他方法與arguments的應用
當函數參數不固定的時候,在函數內部可以通過arguments獲取
//其他方法
function fn(x,y) {
// 函數內部有一個私有變量 arguments(它不是fn.arguments)但跟fn.arguments作用一樣(僞數組,獲取到的是函數的實參)
console.log(arguments);
console.log(fn.arguments); //僞數組,獲取到的是函數的實參,可以根據arguments獲取實參
console.log(fn.caller); //函數的調用者,全局調用時爲null
console.log(fn.name); //函數的名稱,字符串類型
console.log(fn.length); //函數的形參個數
}
function test() {
fn(1,3,6);
}
test();
//當函數參數不固定的時候,在函數內部可以通過arguments獲取
function max() {
var max = arguments[0];
for(var i=0;i<arguments.length;i++){
if(max<arguments[i]){
max=arguments[i];
}
}
return max;
}
console.log(max(1,2,8,9,1,7,3));
高階函數
函數作爲參數與sort的內部實現
// 1、函數作爲參數
function eat(fn) {
setTimeout(function() {
// 喫晚飯
console.log('喫晚飯');
// 喫晚飯後的事情
fn();
}, 2000);
}
eat(function() {
console.log('去唱歌');
});
var arr=[30,55,14,61];
arr.sort(function(a, b) {
return a - b;
})
console.log(arr);
// arr.mySort
Array.prototype.mySort = function (fn) {
for(var i=0;i<this.length-1;i++){
var isSort = true;
for(var j=0;j<this.length-i-1;j++){
if(fn(this[j],this[j+1])>0){
isSort =false;
var temp = this[j];
this[j] = this[j+1];
this[j+1] = temp;
}
}
if(isSort){
break;
}
}
}
var arr=[30,55,14,61];
arr.mySort(function(a,b) {
return a-b;
});
console.log(arr);
函數作爲返回值(可能發生閉包)
- 寫一個函數,第一次調用生成1-10隨機數,以後的每一次調用都返回第一次的隨機值
- 求兩個數的和n是變化的
100+n
1000+n
10000+n
第一個參數也會變化但卻是規定的 - 兩個案例都發生了閉包
//寫一個函數,調用生成1-10隨機整數數
function getRandom() {
return parseInt(Math.random()*10)+1;
}
console.log(getRandom());
// 寫一個函數,第一次調用生成1-10隨機數,以後的每一次調用都返回第一次的隨機值
function getOnceRandom() {
var random=parseInt(Math.random()*10)+1;
return function() {
return random;
}
}
var fn = getOnceRandom();
console.log(fn());
console.log(fn());
console.log(fn());
// 求兩個數的和
// 100+n
// 1000+n
// 10000+n
// 第一個參數也會變化但卻是規定的
function getFun(m) {
return function(n) {
return m+n;
}
}
var fn100=getFun(100);
var fn1000=getFun(1000);
console.log(fn100(15)); //115
console.log(fn1000(15)); //1015
閉包
在一個作用域中可以訪問另一個作用域的變量或者函數
上面兩個案例隨機數和求和都發生了閉包
//未發生閉包
function fn1() {
var n=10;
return n;
}
fn1();
// 閉包
// 特點:延展了函數作用域的範圍
function fun() {
var n=10;
return function() {
return n;
}
}
var f=fun();
console.log(f());
案例1:
輸出點擊元素的索引
方式一更好,因爲外部的自調用函數的作用域並沒有釋放性能變低,所以閉包不一定更好
// 案例1:輸出點擊元素的索引
// 方式1:
var heroes = document.getElementById('heroes');
var list = heroes.children;
for (var i = 0; i < list.length; i++) {
var li = last[i];
li.index = i;
li.onclick = function () {
console.log(this.index);
}
}
// 方式2:使用閉包(第一種更好,因爲外部的自調用函數的作用域並沒有釋放,所以閉包不一定更好)
var heroes = document.getElementById('heroes');
var list = heroes.children;
for (var i = 0; i < list.length; i++) {
var li = last[i];
(function (i) {
li.onclick = function () {
console.log(i);
}
})(i);
}
setTimeout的原理
console.log('start');
setTimeout(function() {
console.log('timeout');
}, 0);
console.log('over');
輸出:start over timeout
案例2:
點擊按鈕改變字體大小
// 案例2:
var btn01 = document.getElementById('btn01');
var btn02 = document.getElementById('btn02');
var btn03 = document.getElementById('btn03');
// btn01.onclick = function () {
// document.body.style.fontSize = '12px';
// }
// btn02.onclick = function () {
// document.body.style.fontSize = '14px';
// }
// btn03.onclick = function () {
// document.body.style.fontSize = '16px';
// }
function makeFun(size) {
return function () {
document.body.style.fontSize = size +'px';
}
}
btn01.onclick = makeFun(12);
btn02.onclick = makeFun(14);
btn03.onclick = makeFun(16);
思考1:
沒有發生閉包
輸出:The Window
var name = "The Window";
var object={
name:'My Obejct',
getNameFun:function () {
return function () {
return this.name;
}
}
}
console.log(object.getNameFun()()); //The Window
思考2:
發生了閉包
輸出:My Obejct
// 思考2:
var name = "The Window";
var object = {
name: 'My Obejct',
getNameFun: function () {
var that =this;
return function () {
return that.name;
}
}
}
console.log(object.getNameFun()()); //My Obejct
遞歸:
一般都要寫一個結束條件,不然會堆棧溢出
案列1:
遞歸實現1+2+3+4+…+n
// 遞歸實現1+2+3+4+...+n
function getSum() {
if (n===1) {
return 1;
}
return n+getSum(n-1);
}
案例2:
獲取斐波那契數列的第n個數
數列:1、1、2、3、5、8、13、21、34
// 斐波那契數列的第n個數
// 數列:1、1、2、3、5、8、13、21、34
function getFB(n) {
if(n===1||n===2){
return 1;
}
return getFB(n-1)+getFB(n-2);
}
淺拷貝:
改變拷貝前的對象,發現普通屬性沒有隨着拷貝前的對象的改變而變化,但是當屬性也是一個對象時,會發生改變
// 對象的淺拷貝
var obj1 = {
name: 'zs',
age: 18,
sex: '男',
dog: {
name: '金毛',
age: 5,
yellow: '黃色'
}
}
var obj2 = {}
// 淺拷貝封裝
function copyObject(o1, o2) {
for (var key in o1) {
o2[key] = o1[key];
}
}
copyObject(obj1, obj2);
obj1.name = 'xxxx';
obj1.dog.name = '大黃';
console.dir(obj2);
深拷貝:
不管屬性是什麼類型,改變拷貝前的對象都不會影響拷貝後的對象
// 深拷貝:
var obj1 = {
name: 'zs',
age: 18,
sex: '男',
dog: {
name: '金毛',
age: 5,
yellow: '黃色'
}
}
var obj2 = {}
// 深拷貝:
function deepCopy(o1,o2) {
for (var key in o1) {
var item = o1[key];
if(item instanceof Object){
o2[key]={};
deepCopy(item,o2[key]);
}else if (item instanceof Array){
o2[key]=[];
deepCopy(item,o2[key]);
}else{
o2[key] = o1[key];
}
}
}
deepCopy(obj1,obj2);
obj1.name = 'xxxx';
obj1.dog.name = '大黃';
console.dir(obj2);
console.log(obj1);