js

创建函数,3种:
1 声明:只有声明方式创建的函数才能被声明提前
function fun(arg1, arg2, ...){ statement; return value;}
2 函数直接量:无法被声明提前
var fun = function(arg1, arg2, ...){ statement; return value;};
揭示:函数其实是一个对象,函数名仅仅是一个引用函数对象的普通变量。
3 用new:
var fun = new Function("arg1", "arg2", ..., "statement; return value;");

重载(overload):相同函数名,不同参数列表的多个函数,在调用时,根据传入参数的不同,自动选择匹配的函数执行
目的目的:减少API的数量,减轻调用者的负担

when:如果多个函数只是流程不同,其实都是一类事,就可以将多个函数命名为同名函数,不同参数
how:js语法不支持重载!js中后定义的同名函数会覆盖先定义的。
arguments:函数中自动接收所有传入函数的参数值的类数组对象。
类数组对象:长得像数组的对象
类数组对象和数组的相同点:都用[i]方式访问每个元素;length属性记录元素的个数;for循环遍历每个元素。

对象的创建,3种:
1 对象直接量:
var obj = {
属性名: 属性值;
...,
方法名: function(){},
...
};
注意:每个属性名和方法名不用加引号,但是底层都是用字符串存储。

this:引用当前正在调用函数的对象!

  和定义在哪个对象中无关!只和调用时   .  前的对象有关!

  固定场景:a.在对象自己的方法中,访问自己的属性

       b.在构造函数中,指代正在创建的新对象

       c.在原型对象的共有方法中,指代将来调用共有方法的子对象

  注意:不是任何对象直接调用的函数中this默认指window对象。
when:对象自己的方法中,想访问对象自己的属性必须用 this.属性名
2 使用new:
var obj = new Object();//创建一个Object类型的空对象
obj.属性名 = 属性值;//等效于 obj["属性名"] = 属性值;
...
obj.方法名 = function(){... this.属性名 ...};
when:如果在创建对象时,暂时不知道包含哪些属性,就先创建一个对象,再添加属性

注意:js中的对象,可以随时添加新属性,只要给不存在的属性赋值,就自动添加

本质:js中一切对象都是hash数组:

  a.都可以随时添加新的属性和方法

  b.访问对象中一个不存在的属性,返回undefined

  c.属性名和方法名其实相当于hash数组中的key

  d.都可用for in遍历每个属性
3 反复创建多个相同类型和结构的对象;
a.定义构造函数,描述一类对象的统一结构
构造函数:描述一类对象的统一结构德函数
when:只要反复创建相同结构的多个对象前,都需要先定义构造函数,再创建对象。
how:
function 类型名/构造函数名(属性参数列表){
this.属性名 = 属性参数值;
...
this.方法名 = function(){
... this.属性名...
}
}
b.使用new调用构造函数创建对象并添加属性
var obj = new 构造函数名(属性值列表);
构造函数中的this,指new正在创建的对象

面向对象:
原型和原型链:
原型:保存所有子对象的公共成员的父级对象

每个构造函数,都有一个prototype属性,引用自己的原型对象
每个子对象,都有一个 __proto__ 属性,继承自构造函数的原型对象

继承:父对象的成员,自对象无需创建,可直接使用!

对象创建:
a.创建空对象
b.设置新对象的 __proto__ 属性继承构造函数的原型对象
c.调用构造函数,添加成员
d.返回地址
when:同一类型的子对象共享的属性和方法,都要放在构造函数的原型对象中。
优点:代码重用,节约内存!

删除属性:
a.自有属性: delete 对象.属性名;
b.共有属性:只能通过构造函数的原型对象删除 delete 构造函数.prototype.属性名

判断自有属性与共有属性:
a.判断自有属性:判断指定属性是否直接保存在当前对象本地
var isSelf = obj.hasOwnProperty("属性名");
true:是自有属性
false:无法确定是否为共有
b.判断共有属性:同时满足两条件:
不是自有: obj.hasOwnProperty("属性名") == false;
但是在原型中有: obj.属性名 !== undefined

<script type="text/javascript">
    
//String类型支持trim(),去掉前后的空格
//如果当前浏览器不支持trim
if(String.prototype.trim === undefined){
    console.log("当前浏览器不支持trim(),自定义trim()");
    String.prototype.trim = function(){
        return this.replace(/^\s+|\s+$/g, "");
    };
}

var str = "    hello js,  I like  ";
console.log(":" + str + ":");
str = str.trim();
console.log(":" + str + ":");

</script>

总结:不使用 obj. 方式访问对象,都去作用域链找,只要使用 obj. 访问成员,都去原型链找。

原型相关API:

1. 获取原型对象,2种:

  a. 构造函数.prototype

  b. 子对象.__proto__  :内部属性,可能被禁用

    尽量使用Object.getPrototypeOf(子对象);

2. 判断对象间是否有继承关系:

  var hasInherit = 父对象.isPrototypeOf(子对象); 

3. instanceof:判断一个对象是否是指定构造函数的实例

  function Student(){};

  var jack = new Student();

  jack instanceof Student -> true

 

多态:同一事物,在不同情况下表现出不同样子

  重写:override:如果子对象觉得父对象的成员不好用,可在本地定义同名成员,覆盖父对象的。

在原型对象中

  如何添加共有属性:只能通过原型对象

    构造函数.prototype.属性名 = 值;

  原型链:由各级对象的 __proto__ 逐级继承形成的链式关系

  规则:在访问对象属性时,只要自己有,就不去父级找,如果自己没有,才去父级找,如果到Object.prototype都没找到,就返回undefined。

  和作用域链的比较:

    作用域链:控制变量的使用顺序:所有不带.的变量,默认都去作用域找

    原型链:控制对象的属性的使用顺序:所有用.访问的对象属性,都去原型链找

  

每个对象内部有一个属性: class 记录了创建对象时使用的类型名
访问对象内部的class: 只能调用原生的toString()
Object.prototype.toString();//"[object Object]"
                                                对象 class

强行调用原生toString():

原生toString.call(替代this的对象)

  call做两件事:a.执行函数;b.替换this

  Object.prototype.toString();     this->Object.prototype

  Object.prototype.toString.call(obj);  this->obj->在执行时,相当于obj.toString()

 

<script type="text/javascript">
    
//判断一个对象是否是数组
var obj1 = {};//Object
var obj2 = [];//Array
var obj3 = function(){};//Function
var obj4 = {}; obj4.__proto__ = [];

//typeof无法区分数组和对象
console.log(typeof obj1);//object
console.log(typeof obj2);//object
console.log(typeof obj3);//function
console.log(typeof obj4);//object
console.log("==================");

//1. isPrototypeOf 不但检查直接父对象,而且检查整个原型链
console.log(Array.prototype.isPrototypeOf(obj1));//false
console.log(Array.prototype.isPrototypeOf(obj2));//true
console.log(Array.prototype.isPrototypeOf(obj3));//false
console.log(Array.prototype.isPrototypeOf(obj4));//true
console.log("==================");

//2. constructor 也可检查整个原型链
console.log(obj1.constructor == Array);//false
console.log(obj2.constructor == Array);//true
console.log(obj3.constructor == Array);//false
console.log(obj4.constructor == Array);//true
console.log("==================");

//3. instanceof 也可检查整个原型链
console.log(obj1 instanceof Array);//false
console.log(obj2 instanceof Array);//true
console.log(obj3 instanceof Array);//false
console.log(obj4 instanceof Array);//true
console.log("==================");

//4. 每个对象内部有一个属性: class 记录了创建对象时使用的类型名
console.log(Object.prototype.toString.call(obj1) == "[object Array]");//false
console.log(Object.prototype.toString.call(obj2) == "[object Array]");//true
console.log(Object.prototype.toString.call(obj3) == "[object Array]");//false
console.log(Object.prototype.toString.call(obj4) == "[object Array]");//false
console.log("==================");

//5. ES5: isArray
if(Array.isArray === undefined){
    console.log("Array.prototype.isArray not exsit.");
    Array.isArray = function(obj){
        return (Object.prototype.toString.call(obj) == "[object Array]");
    };
}
console.log(Array.isArray(obj1));//false
console.log(Array.isArray(obj2));//true
console.log(Array.isArray(obj3));//false
console.log(Array.isArray(obj4));//false

</script>

 

自定义继承:

  a.仅修改一个对象的父对象:

    子对象.__proto__ = 父对象;

    Object.setPrototypeOf(子对象, 父对象);

  b.直接修改构造函数的原型对象,可同时修改之后创建的所有子对象的父对象:

    时机:必须在创建第一个对象之前完成,才能保证对象间的一致性。

  c.两种类型间的继承:即扩展结构(extends),又继承原型(inherit)

    抽象:将多个子类型的相同属性和方法,集中提取到一个公共的父类型中定义。

    how:3步:

      c1. 将多个子类型的相同属性和方法,抽象到一个公共的父类型中集中定义

      c2. 借用构造函数:在子类型构造函数中调用父类型构造函数

        问题:如果直接调用父类型构造函数:this->window

        解决:强行调用,更换this:父类型构造函数.call(this, 参数1, 参数2, ...);

        问题:call要求每个参数都要重复写一遍

        解决:使用apply传入arguments,apply可打散arguments,再单独传入:父类型构造函数.apply(this, arguments);

        总结:只有两个API可打散数组参数: 

          arr1.concat(arr2); -> arr1.concat(arr2[0], arr2[1], ...);

          fun.apply(obj, arr); -> obj.fun(arr[0], arr[1], ...);

      c3. 让子类型的原型继承父类型的原型:Object.setPrototypeOf(子类型的原型, 父类型的原型);

call和apply:

  相同:都是强行调用一个函数,并替换this

  差别:传入参数的方式:

    call:要求每个参数必须单独传入

    apply:要求所有参数以一个数组的方式整体传入

 

<script type="text/javascript">
    
function Flyer(fname, speed){
    this.fname = fname;
    this.speed = speed;
}
Flyer.prototype.fly = function(){
    console.log(this.fname + " is flying, on speed " + this.speed);
};

function Enemy(fname, speed, score){
    Flyer.call(this, fname, speed);
    this.score = score;
}
Enemy.prototype.getScore = function(){
    console.log(this.fname + " score is " + this.score);
};
Object.setPrototypeOf(Enemy.prototype, Flyer.prototype);

function Bee(fname, speed, award){
    Flyer.apply(this, arguments);
    this.award = award;
}
Bee.prototype.getAward = function(){
    console.log(this.fname + " award is " + this.award);
};
Object.setPrototypeOf(Bee.prototype, Flyer.prototype);

var e = new Enemy("enemy001", 50, 20);
e.fly();
e.getScore();

var b = new Bee("bee001", 5, 2);
b.fly();
b.getAward();

</script>

 

*****ES5

对象的属性:

对象:属性的集合

2大类:

  a. 命名属性:自定义的可用,直接访问的属性

    a1. 数据属性:直接保存一个数据的属性

    a2. 访问器属性:专门保护另外一个数据属性的特殊属性,不直接保存数据

  b.内部属性:对象内部自动包含的,无法用.访问到的属性,比如:class

***数据属性:

  ES5中规定,每个数据属性都包含4大特性:

    value:时机存储属性值的特性

    writable:控制当前属性是否可修改 bool

    enumerable:控制当前属性能否被 for in 遍历到 bool

    configurable:控制当前属性能否被删除,或能否修改其他特性

  如何访问属性的特性:

    查看属性的特性: var attrs = Object.getOwnPropertyDescriptor(obj, "属性名");

    设置属性的特性: Object.defineProperty(obj, "属性名", {value: 值, writable: true/false, enumerable: true/false, configurable: true/false});

    注意:一般修改特性时,都要将configurable设置为false:不准修改其他特性;不可逆。

  添加一个新属性,并设置四大属性:

    Object.defineProperty(obj, "属性名", {value: 值, writable: true/false, enumerable: true/false, configurable: true/false});

    注意:使用defineProperty添加的新属性,四大特性默认值为false!

      对象直接量中的属性:四大特性默认值为true

  同时定义或添加多个属性,并设置四大特性:

    Object.defineProperty(obj, {

      "属性名": {value: 值, writable: true/false, enumerable: true/false, configurable: true/false},

      ...

      });

 

<script type="text/javascript">
    
var emp = {id:1001, name: "jack", salary: 10000};
console.log(Object.getOwnPropertyDescriptor(emp, "id"));//Object {value: 1001, writable: true, enumerable: true, configurable: true}
Object.defineProperty(emp, "id", {writable: false});//修改emp的id属性为只读
emp.id = 1002;
Object.defineProperty(emp, "salary", {enumerable: false});//修改emp的salary属性为不可遍历
for(var key in emp){
    console.log(key + ":" + emp[key]);//id:1001   name:jack
}

</script>

 

静态方法:不需要实例化对象,就直接调用的方法。

方法定义在原型对象中,还是定义在构造函数上:

  如果只希望当前类型的子对象才能用,就放在原型对象中;

  如果不希望实例化任何对象,就可以直接调用,就放在构造函数上。

posted @ 2019-05-14 22:45  skorzeny  阅读(89)  评论(0编辑  收藏  举报