javaScript对象以及面向对象

声明:文章的绝大部分内容来自于《javaScript学习指南》和《javaScript忍者秘籍》

在javascript中对象也是中容器,区别数组的是,里面的对象属性是无序的。
个人感觉类似python的字典。

for … in 是枚举对象属性的传统方式:
const SYM = Symbol();
const o = {a:1,b:2,c:3,[SYM]:4};
for(let prop in o){
    document.write(`${prop}:${o[prop]}<br />`);
    `输出:
    a:1
    b:2
    c:3
    `
}
这里有个注意点:for … in不会枚举键为符号的属性。
还有遍历数组推荐使用for…of或者forEach
<script>
const v = [1,2,3,4];
for (let each of v){
document.write(each+"<br \>");
};
`输出如下:
1
2
3
4
`
</script>


Object.keys对象的所有属性,返回的是一个数组。
<script>
`
箭头函数:
x=> x*x*
相当于:
function(x){
    return x*x;
}
`
const SYM = Symbol();
const o = {a:1,b:2,c:3,[SYM]:4};
Object.keys(o).forEach(prop => 
        document.write(`${prop}:${o[prop]}<br />`));

    `输出:
    a:1
    b:2
    c:3
    `
</script>

其他的几种遍历方式,查看链接:
https://www.cnblogs.com/neverleave/p/6134446.html

到了重点的内容:面向对象编程

编程语言的面向对象的思想都是一致的,但是可能实现的方式不一样。
ES6之前没有直接创建类的方法,class关键字是在之后ES6中出现的。
参考:
http://www.jb51.net/article/84089.htm
http://blog.csdn.net/pcaxb/article/details/53759637
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Classes

<script>
class Car{
};
let car1 = new Car();
let  bird = {};
alert(typeof car1);//object
alert(typeof bird);//object
</script>

判断一个实例是否是某个类的: car1 instanceof Car
示例:

<script>
class Person{
    constructor(name,birth){
        this.name=name;
        this.birth=birth;
        this.shenxiao=["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"];
    }
    myshenxiao(){
        let s = (this.birth-1984)%12;
        if (s<0)s +=12;
        alert(`${this.name}:你的生肖是:${this.shenxiao[s]}`);

    }
    showyourself(word){
        alert(word)
    }
}
p1 = new Person("zy",1970);
p1.myshenxiao();//zy:你的生肖是:狗
p1.showyourself("hi girls");//hi girls
</script>
这里有个方法比较特殊的,就是类的构造方法:constructor

this关键字引用了方法被调用时所绑定的实例。

动态属性

大部分面向对象的语言提供了一种机制防止类方法被改写。而javascript没有这种机制,这是缺陷。如上面的代码修改下:

p1 = new Person("zy",1970);
p1.shenxiao=[1,2,3,4,5,6,7,8,9,10,11,12];
p1.myshenxiao();//zy:你的生肖是:11
p1.showyourself("hi girls");//hi girls

他将类属性shenxiao改写了,导致错误的结果。

动态属性可以稍微弥补这种不足。

<script>
class Person{
    constructor(name,birth){
        this.name=name;
        this.birth=birth;
        this._shenxiao=["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"];
    }
    get shenxiao(){ return this._shenxiao}


    myshenxiao(){
        let s = (this.birth-1984)%12;
        if (s<0)s +=12;
        alert(`${this.name}:你的生肖是:${this.shenxiao[s]},${this.birth}`);

    }

}
p1 = new Person("zy",1994);
p1.shenxiao = [1,2,3,4,5,6,7,8,9,10,11,120];
alert(p1.shenxiao);//鼠,牛,虎,兔,龙,蛇,马,羊,猴,鸡,狗,猪
p1.myshenxiao();//zy:你的生肖是:狗,1994

</script>

get和set方法:对某个属性设置存值函数和取值函数, 拦截该属性的存取行为。
但是这种行为就很难说,如下,还是会修改

p1 = new Person("zy",1994);
alert(p1.shenxiao);//鼠,牛,虎,兔,龙,蛇,马,羊,猴,鸡,狗,猪
alert(p1._shenxiao);//鼠,牛,虎,兔,龙,蛇,马,羊,猴,鸡,狗,猪。我就直接去修改p1._shenxiao
p1._shenxiao = [1,2,3,4,5,6,7,8,9,10,11,12];
alert(p1.shenxiao);//[1,2,3,4,5,6,7,8,9,10,11,12];
alert(p1._shenxiao);//[1,2,3,4,5,6,7,8,9,10,11,12];
p1.myshenxiao();//zy:你的生肖是:11,1994

http://www.jb51.net/article/127076.htm

为了类的属性真正私有,先停下说下函数。

《javascript学习指南》中说到(因为我也没去官网看过):class的底层实现并没有发生变化(和之前的es5比),class只是增加了一些语法糖。类实际上还是函数。

<script>
class Person{
}
alert(typeof Person);//输出:function.

</script>

所以更深层的了解函数:
1.无参普通函数

function hello(){
    return 1;
};
t = hello();
alert(t);//1
需要一提的是引用调用:函数名作为是可以赋值给变量的
let f = hello;
f();//等同hello();
//或者
let o = {};
o.f = hello;
o.f();
//或者
let a =[];
a.push(hello);
a[0]();
//或者
const o = {
    name:'zy',
    bark:function(){return 1};
}
//改写如下:es6新特性
const o = {
    name :'zy',
    bark(){return 1}
}
o.bark();

2.有参普通函数:

//javascript中没有函数重载,所以,只要是相同的函数名,那么不管0,1,2,n参数都是不区分的,重新定义的话就会覆盖。
<script>
function hello(a){
alert(2);
}
function hello(){
    alert(1);
};

hello(2);//结果还是1

</script>

//所以带参的函数:
function addAB(a,b){
    return a+b;
};

//不确定参数:使用...来收集
<script>
function addAll(...num){
    let t = 0;
    for ( x of num){
        t = t+x;
    }
    alert(t);
}
addAll(1,2,3,4,5,6);//21
</script>

3.匿名函数和函数表达式

const g =function() {
    ...
}
g();

关于函数和函数表达式的区别:Function Expression 将函数定义为表达式语句(通常是变量赋值)的一部分。通过 Function Expression 定义的函数可以是命名的,也可以是匿名的。

可以注意点:const g = function (){} 和 const g = function f (){}区别:

如果后一种直接调用 f(),直接报错:f is not defined
但是后一种可以自身调用自身:

const g = function f(){
    //代码。。。
    f()
};
g()

4.箭头函数举例:

const f1 = function(){return 1}
const f1 =() => 1;

const  f2 = function(name) {return name}
const f2 = name =>  name;

const f3 = function(a,b){return a+b}
const f3 = (a,b) => a+b;


调用、请求和绑定

http://blog.csdn.net/baidu_36831253/article/details/79310279

函数的作用域:全局作用域、块作用域函数作用域

块:是由一对花括号括起来的一系列语句。那么,块作用域指的就是那些仅仅在代码块内有效的变量。let和const关键字声明的变量名处在块作用域中。

{
    let t = 1;
};
document.write(t);//报错。但是如果使用 var t =1;那么就正常。这就是var的隐患。
重点来了,闭包函数

有一个十分常见的场景:故意将某个函数定义在一个指定的作用域中,并明确地指出它对该作用域所具有的访问权限,通常称这种形式为闭包。
闭包概念:
  闭包就是有权访问另一个函数作用域中变量的函数.

闭包可以让函数访问所有的变量和函数,只要这些变量和函数存在于该函数声明时的作用域内就行。

示例:

<script>
var outvalue =1;
var later;
function outFunc(){
    var innervalue = 2;
    function innerFunc(){
        document.write(outvalue+"<br>");//js当然能访问全局作用域中的变量
        document.write(innervalue);//重点来了,他能访问innerFunc函数之外,但在outFunc之内的变量
    }
    later = innerFunc;
}
outFunc();
later();
`
输出:
1
2
`
</script>

重点是:是什么让innervalue在执行内部函数的时候仍然活着,而且还是在作用域小时之后。答案就是闭包。
在外部函数中声明innerFunc()的时候,不仅是声明了函数,还创建了一个闭包,该闭包不仅包含函数声明,还包含了函数声明的那一时刻点上该作用域中所有变量。
最终当innerFunc执行的时候,当时声明的作用域已经消失了,通过闭包,该函数还是能够访问到原始作用域的。
这里写图片描述

就像一个气泡一样,只要innerFunc函数一直存在,它的闭包就保持该作用域中即将被垃圾回收的变量。
<script>
let outvalue =1 ;
let later;
function outFunc(){
    let innervalue = 2;

    function  innerFunc(paramvalue){
        document.write(outvalue+"<br>");
        document.write(innervalue+"<br>");
        document.write(paramvalue+"<br>");
        document.write(toolate+"<br>");
    };
    later = innerFunc;
}
let  toolate = 4;
outFunc();
later(3);
`输出:
1
2
3
4
`
</script>

再来个示例:并不是一定要函数里面含个函数,才叫闭包。块作用域也可以。

let f;//未定义的函数。
{
    let o = {note:'notsafe'};
    f = function(){
    return o;
    }
}
let out = f();
alert(out.note);//notsafe
即时调用函数表达式:

语法:

(function(){
    //代码;
})();

这里使用函数表达式创建一个匿名函数,然后立即调用该函数。IIFE

const msg = (function(){
    const secret = " dandan";
    return secret;
})();
alert(msg);//dandan

好处:在IIFEzhong ,变量secret是安全的,并且外部不能访问。

函数提升:
<script>
f();
function f(){
alert(1);//1
}
</script>

允许在声明前调用。因为,js预处理

严格模式:

解决问题:如果忘记了使用var声明某个变量,javascript会不假思索地认为开发人员在引用一个全局变量。如果不存在,则自动创建。这是大问题。
为了解决他。可以使用严格模式,在开始编写代码前,单独插入一行字符串“use strict”.如果在全局作用域这么做,整个脚本都会以严格模式执行。

(function(){a =1})();
alert(a);//1
(function(){"use strict";b=2})();
alert(b);//报错:b is not defind
</script>
继续回到我的面向对象:为了让一个类的属性私有。

实现方式:
https://www.cnblogs.com/ihardcoder/p/4914938.html
1)基于强引用散列表的实现方式
2)基于WeakMap的实现方式,而且WeakMap是一种弱引用散列表
这里又要跳开去说下其他的:
+++++++++++++++++++++++++++++

maps和sets

es6新加的特性:两种数据结构:maps和sets。maps跟对象类似,sets则类似数组,但是里面的不能重复值。在es6之前,当需要把键和值映射起来的时候,一般会首选对象。因为对象能把字符串类型的键映射到任意类型的对象。

<script>
//一、将对象进行映射
const u1 = {name:'Cyntina'};
const u2 = {name:'Jackson'};
const u3 = {name:'olive'};
const user= new Map();
user
    .set(u1,'男')
    .set(u2,'女')
    .set(u3,'人妖');
/*等同于下面的
user.set(u1,'男');
user.set(u2,'女');
user.set(u3,'人妖');
*/
alert(user.get(u2));//女


//通过给map的构造函数传一个数组的数组,将包含了数组的数组进行映射。
const people= new Map([
    [u1,'男'],
    [u2,'女'],
    [u3,'人妖']
]);
alert(people.get(u3));//人妖

//如果key已经在map中了,那么调用set()后key对应的value就会被替换。
people.set(u3,'无性人');
alert(people.get(u3));//无性人

//使用has()查看是否有该key
people.has(u1);//true;

//使用size属性返回map中的元素个数
alert(people.size);//3;

//Keys()方法可以拿到map中所有的键,values()可以拿到所有的值,
//entries()可以拿到以数组的方式返回的键值对。

for(let [u,role] of people.entries())
{
    document.write(`${u.name}:${role}`);
    //Cyntina:男Jackson:女olive:无性人
}

//因为entries方法是Map的默认迭代器,所以可以这样写:
for(let [u,role] of people)
{
    document.write(`${u.name}:${role}`);
    //Cyntina:男Jackson:女olive:无性人
}

//delete()方法可以删除map中一个条目
people.delete(u2)

//删除所有条目:
people.clear();
</script>
weak maps
weakmaps跟map在本质上相同的,除了以下几点:
    key必须是对象
    weakmap中的key可以被垃圾回收
    weakmap不能迭代或者清空。

下面这句话解释为啥要用它来进行类的属性私有化:

通常,只要还有地方在引用某个对象,js就会将它保留在内存中。如:如果有一个对象是map中的key,
那么只要这个map存在,这个对象就会一直在内存中。但weakmap却不是这样。正因为如此,
weakmap不能被迭代,因为在迭代过程中,暴露处于垃圾回收过程中的对象,是十分危险的。

将shenxiao的那个程序再次改写如下:

<script>
const Person=(function(){
        const privatePerson = new WeakMap();
        return class{
            constructor(name,birth){
                this.name=name;
                this.birth=birth;
                this._shenxiao=["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"];
                privatePerson.set(this,{shenxiao:this._shenxiao});
            }
            get shenxiao(){return privatePerson.get(this).shenxiao;}

            myshenxiao(){
                let s = (this.birth-1984)%12;
                if (s<0)s +=12;
                alert(`${this.name}:你的生肖是:${this.shenxiao[s]}`);

            }
            showyourself(word){
                alert(word)
            }
        }
})();
p1 = new Person("zy",1970);
alert(p1.shenxiao);//鼠,牛,虎,兔,龙,蛇,马,羊,猴,鸡,狗,猪
p1.shenxiao=[1,2,3,4,5,6,7,8,9,10,11,12];
p1._shenxiao=[1,2,3,4,5,6,7,8,9,10,11,12];
alert(p1.shenxiao);//鼠,牛,虎,兔,龙,蛇,马,羊,猴,鸡,狗,猪
p1.myshenxiao();//zy:你的生肖是:狗
p1.showyourself("hi girls");//hi girls
</script>
对比之前的程序,我们可以发现,之前的如果修改p1._shenxiao还是可以修改p1.shenxiao的。
但是现在两个都试着该,发现,p1.shenxiao改不动。

weak maps的其他示例:

const  Girl =(function(){
    const girl = new WeakMap();
    class meinv{
        setType(yourStyle){
            girl.set(this,yourStyle)
        }

        getGirl(){
            return `你培养的girl是:${girl.get(this)}`;
        }
    }
    return meinv;
})();

const I = new Girl();
I.setType("萝莉");
alert(I.getGirl());//你培养的girl是:萝莉
I.setType("熟女");
alert(I.getGirl());//你培养的girl是:熟女

const you = new Girl();
alert(you.getGirl());//你培养的girl是:undefined
you.setType("淑女");
alert(you.getGirl());//你培养的girl是:淑女

</script>
上面的重点就是:
weak maps中的key可以被垃圾回收,所以出现了undefind,但是普通的Map就不行
那样会导致yourtype一旦设置就难以回收。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值