1. 程式人生 > 實用技巧 >JavaScript面向物件基礎 - 函式與物件

JavaScript面向物件基礎 - 函式與物件

JavaScript面向物件基礎 - 函式與物件

★ JavaScript函式基礎

初識函式

  • 含有預設值的引數
    (function greet(name, say = 'Hi,'){
        console.log(say + name);
    })('ppo!');
  • 剩餘引數
    (function transferParam(num1, ...theNums){
        theNums.unshift(num1);
        console.log(theNums);
    })(1,2,3,4,5,6);

    (function transferParam(...theNums){
        console.log(theNums);
    })(1,2,3,4,5,6);
  • 函式的呼叫
    function getSum(){
        var sum = 0;
        for(var i in arguments){
            sum += arguments[i];
        }
        return sum;
        
    }
    console.log(getSum(1,2,3,4,5,6));
  • 垃圾回收機制
    • 由於字串、物件和陣列沒有固定大小,所有當他們的大小已知時,才能對他們進行動態的儲存分配。JavaScript程式每次建立字串、陣列或物件時,直譯器都必須分配記憶體來儲存那個實體。只要像這樣動態地分配了記憶體,最終都要釋放這些記憶體以便他們能夠被再用,否則,JavaScript的直譯器將會消耗完系統中所有可用的記憶體,造成系統崩潰。
    • 若要在開發中保留區域性變數的值。可以利用return返回,或者通過全域性變數儲存。
    var num = (function sum(num){
        num += 1;
        return num;
    })(20);
    console.log(num);

    var money;
    (function total(num){
        money = num + 1;
    })(20);
    console.log(money);

匿名函式

  • 函式表示式中省略函式名
    var fn = function(num1, num2){
        return num1 + num2;
    }
    console.log(fn(1, 2));
  • 自呼叫方式
    var res = (function(num1, num2){
        return num1 + num2;
    })(1, 2);
    console.log(res);
  • 處理事件
    document.onclick = function(){
        alert('你好,世界!');
    }
  • 箭頭函式 - 標準語法
    ((p1,p2,p3) => {
        console.log(p1+ p2+ p3);
    })(1,2,3);
  • 箭頭函式 - 返回值
    var sum = ((p1,p2,p3) => {
        return p1+p2+p3;
    })(1,2,3);
    console.log(sum);
  • 箭頭函式 - 一個引數
    (x => {
        console.log(x);
    })("你好,世界!");
  • 箭頭函式 - 兩個引數
    ((x, y) => {
        console.log(x+ y);
    })("你好,", "世界!");
  • 箭頭函式 - 無參函式
    (() => {
        console.log("你好,");
    })();

    (_ => {
        console.log("世界!");
    })();

回撥函式

  • 案例
    function cal(a, b, fn){
        return fn(a, b);
    }
    function add(num1, num2){
        return num1+ num2;
    }
    function mul(num1, num2){
        return num1* num2;
    }
    console.log(cal(45, 5, add));
    console.log(cal(45, 5, mul));
  • 陣列回撥函式API
    • find() - 返回陣列中滿足回撥函式的第一個元素的值,否則返回undefined
    • every() - 測試陣列的所有元素是否都通過了回撥函式的測試
    • some() - 測試陣列中的某些元素是否通過由回撥函式實現的測試
    • forEach() - 對陣列的每個元素執行一次提供的函式
    • map() - 建立一個新陣列,其結果是該陣列中的每個元素都呼叫一次提供的回撥函式後返回的結果
    • reduce() - 對累加器和陣列中的每個元素(從左到右)應用一個函式,將其減少為單個值
    • reduceRight() - 接收一個函式作為累加器(accumulator)和陣列的每個值(從右到左)將其減少為單個值
var arr = ['a', 'b', 'c'];
arr.map((value, index) => {
    console.log(value, index);
});

遞迴函式

  • 階乘案例
    function factorial(n){
        if(n== 1){
            return 1;
        }else{
            return n* factorial(n- 1);
        }
    }
    var n= parseInt(prompt('請輸入階乘數:'));
    if(isNaN(n)){
        console.log('n值輸入不合法');
    }else{
        console.log(factorial(n));
    }
  • 斐波那契數列第N項的值
    var recursion= (n)=> {
        if(n< 0){
            return '輸入的數字不能小於0';
        }else if(n== 0){
            return 0;
        }else if(n== 1){
            return 1;
        }else if(n> 1){
            return recursion(n- 1)+ recursion(n- 2);
        }
    }
    console.log(recursion(10));

閉包函式

  • 作用域鏈
    • 由於作用域是相對於變數而言的,而如果存在多級作用域,這個變數又來自於哪裡?這個問題就需要好好地探究一下了,我們把這個變數的查詢過程稱之為變數的作用域鏈
    • 作用域鏈的意義:查詢變數(確定變數來自於哪裡,變數是否可以訪問)
    • 簡單來說,作用域鏈可以用以下幾句話來概括:(或者說:確定一個變數來自於哪個作用域)
      • 檢視當前作用域,如果當前作用域聲明瞭這個變數,就確定結果
      • 查詢當前作用域的上級作用域,也就是當前函式的上級函式,看看上級函式中有沒有宣告
      • 再查詢上級函式的上級函式,直到全域性作用域為止
      • 如果全域性作用域中也沒有,我們就認為這個變數未宣告(xxx is not defined)
    var name = 'sunny';
    (()=>{
        var name = 'storm';
        console.log(name);
    })();

    var name = 'sunny';
    (()=>{
        console.log(name);
        var name = 'storm';
    })();

    var name = "sunny";
    var f1 = ()=> {
        return ()=>{
            console.log(name);
        }
        var name="storm";
    }
    var nameNew = f1();
    nameNew();

    var name = "sunny";
    var f1 = ()=>{
        return {
            say:()=>{
                console.log(name);
                var name = "storm";
            }
        }
    }
    var nameNew = f1();
    console.log(nameNew.say());
  • 閉包函式的實現
    • 函式執行完畢後,作用域中保留了最新的timer變數的值
    • 閉包函式常應用於模組化開發,防止變數被破壞
    var count = ()=>{
        var timer = 0;
        var c = ()=>{
            return timer++;
        }
        return c;
    }
    var counter = count();
    console.log(counter());
    console.log(counter());
    console.log(counter());
  • 函式的四種呼叫方式
    • 在ES6的箭頭函式之前的時代,想要判斷一個函式內部的this指向誰,就是根據這四種方式來決定的,函式內部的this跟大小寫、書寫位置無關
    • 函式呼叫
    • 方法呼叫
    • 建構函式呼叫(new)
    • 上下文方式呼叫(call、apply、bind)
    // 函式呼叫,this指向window
    var age = 18;
    var person = {
        age:15,
        say:function(){
            console.log('我今年' + this.age + '歲!');
        }
    }
    var sunny = person.say;
    sunny(); 
    // 方法呼叫,this指向物件person
    var age = 18;
    var person={
        age:15,
        say:function (){
            console.log('我今年' + this.age + '歲!');
        }
    }
    person.say();
    // 建構函式呼叫,this將指向建構函式例項本身
    var age=18;
    var person={
        age:15,
        say:function(){
            console.log('我今年' + this.age + '歲!');
        }
    }
    new person.say();
    //上下文呼叫方式,有3種,call、apply、bind
    function f1(){
        console.log(this);
    }
    //call方法的第一個引數決定了函式內部的this的值
    f1.call([1,3,5])
    f1.call({age:20,height:1000})
    f1.call(1)      
    f1.call("abc")
    f1.call(true);
    f1.call(null)
    f1.call(undefined);

    //上述程式碼可以用apply完全替換

    //總結:
    //call方法的第一個引數:
    //1、如果是一個物件型別,那麼函式內部的this指向該物件
    //2、如果是undefined、null,那麼函式內部的this指向window
    //3、如果是數字-->this:對應的Number建構函式的例項
    //      -->   1   --> new Number(1)
    //  如果是字串-->this:String建構函式的例項
    //      --> "abc"   --> new String("abc")
    //  如果是布林值-->this:Boolean建構函式的例項
    //      --> false   --> new Boolean(false)
  • bind()函式
    // 普通方法呼叫
    var person = {
        age:18,
        run : function(){
            console.log(this);  //this指向person
            var _that=this;
            setTimeout(function(){
                console.log(this.age); //this指向window
                console.log(_that.age); 
            },50);
        }
    }
    person.run();
// 通過執行了bind方法,匿名函式本身並沒有執行,只是改變了該函式內部的this的值,指向person
var person = {
    age:18,
    run : function(){
        console.log(this);  // this指向person
        setTimeout((function(){
            console.log(this.age); 
        }).bind(this),50);  // 繫結this指向person
    }
}
person.run();
    // bind函式基本用法
    function speed(){
        console.log(this.seconds);
    }
    speed({ seconds:100 });
    // 執行了bind方法之後,產生了一個新函式,
    // 這個新函式裡面的邏輯和原來還是一樣的,唯一的不同是this指向{ seconds:100 }
    var speedBind = speed.bind({ seconds:100 });
    speedBind();    //100
    // bind函式常規寫法
    (function eat(){
        console.log(this.seconds);
    }).bind({ seconds:360 })()  
    // bind函式案例
    var obj={
        name:"西瓜",
        drink:(function(){
            //this指向了:{ name:"橙汁" }
            console.log(this.name);
        }).bind({ name:"橙汁" })
    }
    obj.drink();    //"橙汁"

    var p10={
        height:88,
        run:function(){
            //this
            setInterval((function(){
                console.log(this.height);   //88
            }).bind(this),100)  
        }
    }
    p10.run();
    // 手寫bind函式
    Function.prototype._bind = target => {
        // 這裡的this指向函式例項
        // target表示新函式的內部的this的值
        // 利用閉包建立一個內部函式,返回那個所謂的新函式
        return () => {
            this.call(target);
        }
    }
    function person(){
        console.log(this);
    }
    person();
    var sunny = person.bind({age:18});
    sunny();

★ JavaScript物件基礎

初識物件

  • 面向物件的特徵

    • 封裝性、繼承性、多型性
    • 物件是鍵值對的集合:物件是由屬性和方法構成的 (ps:也有說法為:物件裡面皆屬性,認為方法也是一個屬性)
  • 物件屬性操作

    var student = {
        name: 'sunny',
        age: 20,
        number: '11034P',
        department: 'newEnergy',
        score: function(){
            return '成績查詢系統!';
        },
        job: function(){
            return '就業查詢系統!';
        }
    }
    var arr = [];

    // .語法
    // .語法後面不能使用js中的關鍵字、保留字(class、this、function。。。)
    // .語法後面不能使用數字
    // .語法後面可直接執行方法函式
    console.log(student.name);
    console.log(student.score());

    // []語法
    // []內部必須用引號引入變數名 (student['number']
    // ["class"]、["this"]等保留字都可以隨意使用
    // [0]、[1]、[2]也可以使用 ? 為什麼obj[3]==obj["3"]
    // 甚至還可以這樣用:["[object Array]"]  jquery裡面就有這樣的實現
    // 也可以這樣用:["{abc}"]  給物件添加了{abc}屬性
    console.log(student['age']);
    console.log(student['job']);

    // 新增屬性
    student['[object Array]'] = 240;
    student['{object}'] = {'arts':85, 'sports': 95};
    for(key in student){
        arr.push(key);
    }
    console.log(student);
    console.log(arr);
    arr = [];

    // 修改屬性
    student.gender = 'man';
    student['direction'] = function(){
        return '人工智慧與圖形操作';
    }
    for(key in student){
        arr.push(key);
    }
    console.log(student);
    console.log(arr);
    arr = [];

    // 刪除屬性
    delete student.number;
    for(key in student){
        arr.push(key);
    }
    console.log(student);
    console.log(arr);
    arr = [];
  • 獲取物件長度的方法
    • 物件的長度不能用.length獲取
    • Object具有一個keys屬性,可以返回json物件的key的陣列
    • Object可以使用in運算子
    var student = {
        name: 'sunny',
        age: 20,
        number: '11034P',
        department: 'newEnergy',
        score: function(){
            return '成績查詢系統!';
        },
        job: function(){
            return '就業查詢系統!';
        }
    }

    var arr = [];

    arr = Object.keys(student);
    console.log(arr.length);
    arr = [];

    for(key in student){
        arr.push(key);
    }
    console.log(arr.length);
    arr = [];

    console.log('name' in student);
  • 物件的淺拷貝與深拷貝
    // 淺拷貝
    var student = {
        name: 'sunny',
        age: 20,
        number: '11034P',
        department: 'newEnergy',
        score(){
            return '成績查詢系統!';
        },
        job(){
            return '就業查詢系統!';
        }
    }
    var simple = obj=>{
        let newObj = {};
        for(let key in obj){
            newObj[key] = obj[key];
        }
        return newObj;
    }
    var sunny = simple(student);
    console.log(sunny);
    // 深拷貝
    var student = {
        name: 'sunny',
        age: 20,
        number: '11034P',
        department: 'newEnergy',
        score: {
            arts: 95,
            sports: 85,
            program: 95
        },
        job(){
            return '就業查詢系統!';
        }
    }

    var deepCopy = obj=>{
        var newObj = {};
        for(let key in obj){
            newObj[key] = (typeof obj[key] === 'object') ? deepCopy(obj[key]) : obj[key];
        }
        return newObj;
    }
    var sunny = deepCopy(student);
    console.log(sunny);

建構函式

  • 建立建構函式
    var student = {};
    student.name = 'sunny';
  • 內建的建構函式
    var obj = new Object();
    var num = new Number();
    var str = new String();
    var now = new Date();
    var arr = new Array();
    var reg = new RegExp();
    var bool = new Boolean();
    var func = new Function();
    var img = new Image();


    console.log(obj);
    console.log(obj.constructor);

    console.log(num);
    console.log(num.constructor);

    console.log(str);
    console.log(str.constructor);

    console.log(now);
    console.log(now.constructor);

    console.log(arr);
    console.log(arr.constructor);

    console.log(reg);
    console.log(reg.constructor);

    console.log(bool);
    console.log(bool.constructor);

    console.log(func);
    console.log(func.constructor);

    console.log(img);
    console.log(img.constructor);