对象包装类原形原型链


对象

用已学的知识点,描述一下你心目中的对象

var mrDeng = {
    name : "mrDeng",
    age : 40,
    sex : "male",
    health : 100,//可在控制台更改
    smoke : function () {
        console.log('I am smoking');
        mrDeng.health --;
    },
    drink : function () {
        console.log('I am drink');
        mrDeng.health ++;
    }
}

改进 第一人称:this

var mrDeng = {
    name : "mrDeng",   千万别是等于号
    age : 40,
    sex : "male",
    health : 100,//可在控制台更改
    smoke : function () {
        console.log('I am smoking');
        this.health --;
    },
        drink : function () {
            console.log('I am drink');
            this.health ++;
        }
}

1.属性的增、删、改、查

drink:function(){
    console.log('I am drinking');
    this.health ++;
}
meDeng.wife = "xiaowang";
// console.log(mrDeng.wife = "xiaoliu");
// console.log(mrDeng);

console.log(mrDeng);
console.log(mrDeng.sex = "male");
console.log(mrDeng.sex);

删 delete + 属性

当一个变量没有声明就是用报错,对象的属性没有就访问打印 undefined,不会报错

var deng = {
    prepareWife : "xiaowang",
    name : "laodeng",
    sex : "male",
    gf : "xiaoliu",
    wife : "",
    divorce : function () {
        delete this.wife;
        this.gf = this.PrepareWife;

    },
    getMarried : function () {
        this.wife = this.gf;

    },
    changePrepareWife : function (someone){
        this.PrepareWife = someone;
    }
}

2.对象的创建方法

1.var obj = {} 叫 plainObject 对象字面量/对象直接量

2.构造函数

(1) 系统自带的构造函数 new Object

var obj = new Object();
obj.name = 'abc';
obj.sex = 'female';
obj.say = function(){
}
var obj = {
    name : ""
}

系统提供的:new Object();Array();Number();Boolean();Date();

(2) 自定义

function Car(){//方便使用-人为的,构造函数特点:大驼峰式命名规则,只要是单词,首字母大写(小驼峰式:第一个外的首字母大写)
    this.name = "BMW";
    this.height = "1400";
    this.lang = "4900";
    this.weight = 1000;
    this.health = 100;
    this.run = function (){
        this.health --;
    }
}
var car = new Car();//每个都一样,但是每个都独立
var car1 = new Car();//不是一个人,不互通
console.log(car.run());
console.log(car1.health);
console.log(car.health);

demo

实现自己选配颜色

function Car(color){
    this.color = color;
    this.name = "BMW";
    this.height = "1400";
    this.lang = "4900";
    this.weight = 1000;
    this.health = 100;
    this.run = function (){
        this.health --;
    }
}
var car = new Car('red');
var car1 = new Car('green');
console.log(car/car1);
function Student(name,age,sex) {
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.grade = 2017;
}
var student = new Student('zhangsan',18,'male');

注意事项

var obj = new Object(){
    obj.name = 'abc';
    obj.sex = 'female';
    obj.say = function(){
    }
}
var obj = {
    name : ""//冒号
}

构造函数内部原理

有 new 则发生三步

1.在函数体最前面隐式的加上 var this = {} 2.执行 this.xxx = xxx; 3.隐式的返回 this

function Student(name,age,sex) {
    //var this = {
    // name : ""
    // age :
    // };
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.grade = 2017;
    // return this;
}
var student = new Student('zhangsan',18,'male');
function Person(name, height){
    // var this = {}
    this.name = name;
    this.height = height;
    this.say = function (){
        console.log(this.say);
    }
    // return this;
}
console.log(new Person('xiaoliu',180).name);

模拟构造函数

function Person(name, height){
    var that = {};
    that.name = name;
    that.height = height;
    return that;
}
var person = new Person('xiaowang', 180);
var person1 = new Person('xiaozhang',175);

冷门知识

function Person(name, height){
    var this = {};
    this.name = name;
    this.height = height;
    this.say = function	(){
        console.log(this.say);
    }
    return {};//显示返回空对象,so person很person1都返回空对象
    // return this;
}
var person = new Person('xiaowang', 180);
var person1 = new Person('xiaozhang',175);
// 但是return一个原始值不允许

Object.create(原型)方法

包装类

1.小知识

原始值不能有属性和方法,只有对象能有,对象包括对象自己,数组,function。
数字不一定是原始值。数字分两种:原始值数字才是原始值。数字,字符串分为两种。
var num = 123;数字
Var num = new number(123);也数字,对象 123

console.log(num);
console.log(num.abc='a');
console.log(num.abc);
console.log(num);
console.log(num*2);//成了数字,没有了对象属性,同理字符串,布尔

不能有属性的两个原始值:undefined null

String();
Boolean();
Number();

var num = new Number(123);
var str = new String('abcd');
var bol = new Boolean('true');
// undefined与null不可以有属性
console.log(num.abc);

2.现象:

var str = “abcd”;
str.length = 4;//理论上不可以
str.abc = ‘a’;
str.abc = undefined;

原始值不可能有属性和方法,因为经历了一个过程——包装类,才能调用

var num = 4;//包装类
console.log(num.len = 3);
//new Number(4).len = 3;  delete
// new Number(4).len
console.len(num.len);//undefined
// num没有length

考题

基于一个理论:数组截断

var arr = [a,b,c,d];
console.log(arr.length = 2);
console.log(arr);

成哥真题

var str = "abcd";
str.length = 2;
// new String('abcd').length = 2;delete
console.log(str);//abcd

变式

var str = "abcd";
str.length = 2;
// new String('abcd').length = 2;delete
// new String('abcd').length
console.log(str.length);//4

原形

1.定义

原型是 function 对象的一个属性,它定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法。原型也是对象。

构造函数产生的对象:

// Person.prototype = {} 是祖先
Person.prototype.name = "hehe";
function Person() {
}
var person = new Person();

应用:

实例一:自己有取自己的

Person.prototype.LastName = "Deng";
Person.prototype.say  = function(){
    console.log('hehe');
}
function Person() {
    // this.LastName = 'ji';先看自己后看父亲
}
var person = new Person();
var person1 = new Person();

实例二:

Person.prototype.LastName = "Deng";
Person.prototype.say  = function(){
    console.log('hehe');
}
function Person(name,age,sex) {
    this.name = name;
    this.age = age;
    this.sex = sex;
}
var person = new Person('xuming',30,'male');

2.提取共有属性。

function Car(color,owner) {
    this.owner = owner;
    this.carName = "BMW";//每次生产都得执行这三句,耦合
    this.height = 1400;
    this.lang = 8900;
    this.color = color;
}
var car = new Car('red','prof.ji');

优化

Car.prototype.height = 1400;
Car.prototype.lang = 8900;
Car.prototype.carName = "BMW";
function Car(color,owner) {
    this.owner = owner;
    this.color = color;
}
var car = new Car('red','prof.ji');
var car1 = new Car('green','laodeng');

3.原形的增删改查

Person.prototype.lastName = "Deng";//原形属性没有改,想改,就要调用Person.prototyoe.lastName
function Person(name) {
    this.name = name;
}
var person = new Person('xuming');
console.log(person.lastName="james");//这不叫修改,这叫增加
console.log(person);
console.log(person.lastName);

console.log(delete person.name);
console.log(person.name);
console.log(delete person.lastName);
console.log(person.lastName);//删除失效

优化

Car.prototype.height = 1400;
Car.prototype.lang = 8900;
Car.prototype.carName = "BMW";
一步到位
Car.prototype = {
    height : 1400,
    lang : 4900,
    carName : "BMW"
}

2.对象如何查看对象的构造函数 — > constructor 构造器

function Person(){

}
//可以手动更改
//构造器constructor:谁生的
Car.prototype = {
    constructor : Person
}
function Car(){

}
var car = new Car();
console.log(car.constructor);

  1. 对象如何查看原型 — > 隐式属性 proto
function Person() {}
var person = new Person(); //浅紫色,隐式命名规则
console.log(person);
person.prototype.name = 'abc';
function Person(){

}
var person = new Person();
// 私人属性:var __pravite
console.log(person.__proto__)//__proto__里面放的是原形
Person.prototype.name = 'abc';
function Person(){
    var this = {
        // __proto__: Person.prototype
    }
}//先看看自己有没有name属性,没有的话沿着proto指向找
var perosn = new Person();
console.log(perosn.name);

改变 proto 指向会使得指向更改

Person.prototype.name = 'abc';
function Person() {
    // var this = {
    // 	// __proto__: Person.prototype
    // }
}
var obj = {
    name: "sunny"
}
var person = new Person();//换爹
person.__proto__ = obj;

演示 1

Person.prototype.name = "sunny";
function Person() {

}
var person = new Person();
Person.prototype.name = "cherry";
person.name;
分析:自己没有,找proto,是Person.prototype:sunny,最后又改成cherry

演示 2

Person.prototype.name = "sunny";
function Person() {

}
Person.prototype.name = "cherry";
var person = new Person();
console.log(person.name);
同理分析cherry

演示 3

Person.prototype.name = "sunny";
function Person() {

}
var person = new Person();
Person.prototype = {
    name : "cherry"
}
person.name;
答案:sunny。
.的写法是原有的基础上把原有的值改了,这一次是把原形改了,换了个新对象

简化:引用值的互相赋值

var obj = {name : "a"};
var obj1 = obj;
obj = {name : "b"};
obj1:a,obj:b
Person.Prototype.name = "sunny";
function Person() {
    //var this = {__proto__:Person.prototype}
}//proto指向不变还是sunny
var person  = new Person();
Person.prototype = {//把自己空间换了
    name:'cherry'
}
// 更加简化
// Perosn.prototype = {name:"a"};
// __proto__ = Person.Prototype;
// Person.Prototype = {name:"b"};

演示 4

Person.prototype.name = "sunny";
function Person() {
}
Person.prototype = {
    name: "cherry"
}
var person = new Person();
person.name;
答案:cherry
预编译:函数 function Person() { } 提升到最上面,然后顺序执行,执行到最后一行,有new了,
就有function Person() { var this = __proto__ Person.prototype } ,再访问:下面的把上覆盖

原型链引入

Grand.prototype.LastName = "Deng";
function Gand(){
}
var grand = new Grand();
Father.prototype = grand;
function Father(){
    this.name  = "xuming";
}
var father = new Father();
Son.prototype = father;
function Son(){
    this.hobbit = "somke";
}
var son = new Son();
console.log(son.hobbit);
console.log(son.name);//顺着链找
console.log(son.toString);
Grand.prototype.__proto__=Object.prototype是所有对象的最终原形
console.log(Object.prototype);
console.log(Object.prototype.__proto__);proto没了,so就是终端

原型链

如何构成原型链?

1.原型链上属性的增删改查

增,删,修改:只有本人有权限,子孙没有

console.log(delete Father.prototype.n);//删不了

特例:引用值调用修改

var grand = new Grand();
Father.prototype = grand;
function Father() {
    this.name = 'xuming';
    this.fortune = {
        card1 : 'visa'
    }
}
var father = new Father();
Son.prototype = father;
function Son(){
    this.hobbit = "somoke";
}
var son =  new Son();
console.log(son.fortune);
son.fortune = 200;
console.log(son);
console.log(father.fortune);
son.fortune.card2 = 'master';
cons.log(father);

demo

Grand.prototype.LastName = "Deng";
function Grand(){

}
var grand = new Grand();
Father.prototype = grand;
function Father(){
    this.name = "xuming";
    this.forture = {
        card1 : 'visa'
    };
    this.num = 100;
}
var father = new Father();
Son.prototype = father;
function Son(){
    this.hobbit = "smoke";
}
var son = new Son();
console.log(son.num++);100
console.log(father);100
console.log(son.num);101

演示

Person.prototype = {
    name : "a",
    sayName : function(){
        console.log(this.name);
    }
}
function Person(){

}
var person = new Person();//答案:a
Person.prototype = {
    name : "a",
    sayName : function(){
        console.log(this.name);
    }
}
function Person(){
    this.name = "b";
}
var person = new Person();

小常识:
a.sayName()
sayName 里面的 this 指向是,谁调用的这个方法,this 就指向谁
person.sayName(); person 调用的,b
如果 Person.prototype.sayName(); a

Person.prototype = {
    height : 100
}
function Person(){
    this.eat = function(){
        this.height ++;
    }
}
var person = new Person();
console.log(person.eat);
person.eat();
console.log(person.eat);//默認return:undefined 查看要写代码

公司规范:数组,对象都用字面量创建

var obj = {};//对象自变量创建形式,有原形
//与var obj1 = new Object();相同
var obj = {};内部来一个new Object()
所以要写对象字面量

绝大多数对象的最终都会继承自 Object.prototype

(选择题真题)
例外:由于 Object.create

console.log(Object.create());//报错,不写代码
console.log(Object.create(null));

Object.create(原型)也能创造对象

// var obj = Object(原形);
var obj = {name : "sunny", age : 123};
var obj1 = Object.create(obj);
// obj1成为了对象,obj1的原形是obj,所以obj1.name就是obj.name
Person.prototype.name = "sunny";
function Person() {
}
var person = Object.create(Person.prototype);

2.关于 toString:

只有 undefined 与 null 不能调用 toString
数字可以,因为经过包装类一层层访问 123.toString();
undefined 没有包装类,是个原始值,没有原形,不能调用

console.log(undefined.toString);
console.log(null.toString);
obj.__proto__ = { name: sunny};
console.log(obj.name);//自己加原形不管用

各个变量各个属性值调用 toString 返回结果不一样 变成字符串

var num = 123;
console.log(num.toString);//字符串
console.log(123.toString);//识别成浮点型

现象

var obj = {};
obj.toString();---->[object Object]

原因

var num = 123;
// num.toString();-->new Number(num).toString();
Number.prototype.toString = function (){

}
// Number.prototype.__proto__ = Object.prototype

重写笔试题:写一个方法重写形式,写一个和系统同样的名,不同功能

// Object.prototype.toString = function (){
// }
Person.prototype = {
    toString : function () {
        return 'hehhe';
    }
}
function Person () {
}
var Person = new Person();

本集多看弱项

3.小 bug

console.log(0.4*100);//js精度不准
console.log(Math.ceil(123.234));//向上取整
console.log(Math.floor(123.99999));//下

随机生成 0-100 随机数

Math.random();//随机数生成函数(0,1)
for(var i = 0;i < 10; i++){
    var num  = Math.random().toFixed(2)*100;
    console.log(num);//会出现偏差
}

解决:先*100 后取整

for(var i = 0; i < 10; i++){
    var num = Math.floor(Math.random() * 100);
    console.log(num);
}
// 总结:可正常那个计算的范围:小数点前16后16

call/apply 必考

1.call

作用,改变 this 指向。借用别人的函数实现自己的功能
区别,后面传的参数形式不同。
call 需要把实参按照形参的个数传出去 apply 需要传一个 arguments

function test(){
}
test()===test.call();//后面隐式默认

call 里面可以传东西

function Person(name, age){
    this.name = name;
    this.age = age
}
var person = new Person('deng',100);
var obj = {
}
// Person.call(obj);
// 会让Person里所有的this变成obj 即this=obj
// 怎么传参:Person.call(obj,'cheng',300);
Person.call(obj,'cheng',300);

企业级开发

//开发讲究快准狠,A写的代码能实现B的功能,省时
function Person(name, age, sex) {
    this.name = name;
    this.age = age;
    this.sex = sex;
}
function Student(name, age, sex, tel, grade){
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.tel = tel;
    this.grade = grade;//后面覆盖前面
}
// call 借用别人的函数实线自己的功能
function Person(name, age, sex) {
    this.name = name;
    this.age = age;
    this.sex = sex;
}
function Student(name, age, sex, tel, grade){
    // var this = {name : "", age : "",sex : ""}
    Person.call(this, name, age, sex);
    this.tel = tel;
    this.grade = grade;
}
var student = new Student('sunny',123,'male',139,2017);

造车举例

function Wheel(wheelSize,style) {
    this.style = style;
    this.wheelSize = wheelSize;
}
function Sit(c,sitColor) {
    this.c = c;
    this.sitColor = sitColor;
}
function Model(height,width,len) {
    this.height = height;
    this.width = width;
    this.len = len;
}
function Car(wheelSize, style, c, sitColor, height, width, len) {
    Wheel.call(this, wheelSize,style);
    Sit.call(this,c,sitColor);
    Model.call(this, height, width, len);
}
var car = new Car(100,'花里胡哨的','真皮','red',1800,1900,4900);

2.apply:

apply 只能传送一个数组形式的实参
Wheel.apply(this, [wheelSize,style]);

总结:

call:需要把实参按照形参的个数传进去

apply: 需要传一个 arguments

作业:电子书 js 设计模式 0-35 页上部分

笔试题:用友 2017 校招前端

继承发展史

1.传统形式

过多的继承了没用的属性

Grand.prototype.lastName = "Ji";
function Grand() {
}
var grand = new Grand();
Father.prototype = grand;
function Father() {
    this.name = "hehe";
}
var father = new Father();
Son.prototype = father;
function Son() {
}
var son = new Son();//一系列从头到尾继承,导致不想继续的也继承了

2.借用构造函数

不能继承借用构造函数的原型
每次构造函数都要多走一个函数实际浪费效率

function Person(name,age,sex){
    this.name = name;
    this.age = age;
    this.sex = sex;
}//实际上call apply不是继承
function Student(name, age, sex, grade) {
    Person.call(this,name,age,sex);
    this.grade = grade;
}
var student = new Student();

3.共享原型

不能随便改动自己的原型

Father.prototype.lastName = "Deng";
function Father(){
    // Son想继承father的prototype
}
function Son(){
}

实现方法

  1. 原型链

    function Son()上面放上 var father…

  2. 共有原形

Father.prototype.lastName = "Deng";
function Father() {
}
function Son() {
}
Son.prototype = Father.prototype
var son = new Son();
var father = new Father();

抽象成函数

Father.prototype.lastName = "Deng";
function Father() {

}
function Son() {

}
function inherit(Target, Origin) {
    Target.prototype = Origin.prototype;
}
inherit(Son, Father);
var son = new Son();//son.LastName = "Deng";

要先继承后使用,Son.lastName 指向原来的空间

Father.prototype.lastName = "Deng";
function Father() {

}
function Son() {

}
function inherit(Target, Origin) {
    Target.prototype = Origin.prototype;

}
var son = new Son();
inherit(Son, Father);	//先继承,后该原形不管用 son.lastName = undefined

不足:

son 给自己多加一个属性,方便后续生产出的对象使用,不能个性实现,继承了,但是影响

Father.prototype.lastName = "Deng";
function Father(){

}
function Son(){

}
function inherit(Target, Origin){
    Target.prototype = Origin.prototype;

}
inherit(Son, Father);
Son.prototype.sex = "male";//与father的prototype一致,一个变,都变
Father.prototype
var son = new Son();
var father = new Father();
// 要实现:想继承但不相互影响

4.圣杯模式

通过原型链

function inherit(Target, Origin) {
    function F(){};
    F.prototype = Origin.prototype;
    Target.prototype = new F();
}
Father.prototype.lastName = "Deng";
function Father(){

}
function Son() {

}
inherit(Son,Father);
var son = new Son;
var father = new Father();
console.log(Son.prototype.sex = "male");
console.log(son.sex);
console.log(father.sex);
console.log(Father.prototype);

但是,constructor 应该指向构造函数,然而,son.constructor = ƒ Father()????怎么回事?

原形上系统自带的一个属性叫 constructor,默认指向他的构造函数

son.__proto__ --->new F().__proto__--->Father.prototype
//指向紊乱了

实现继承:必会圣杯模式

function inherit(Target, Origin){
    function F(){};
    F.prototype = Origin.prototype;
    Target.prototype = new F();
    Target.prototype.constructor = Target;
    Target.prototype.uber = Origin.prototype;//超级父级:超类
}
Father.prototype.lastName = "Deng";
function Father(){
}
function Son(){
}
inherit(Son, Father);
var son = new Son();
var father = new Father();

颠倒

F.prototype = Origin.prototype;
Target.prototype = new F();//不能颠倒,一定要在new之前改原形

类雅虎

闭包的作用:可以实现封装,属性私有化

function Deng(name, wife){
    var prepareWife = "xiaozhang";
    this.name = name;
    this.wife = wife;
    this.divorce = function(){
        this.wife = prepareWife;
    }
    this.changePrepareWife = function(target){
        prepareWife = target;
    }
    this.sayPreparewife = function(){
        console.log(prepareWife);
    }
}
var deng = new Deng('deng','xiaoliu');
console.log(deng.sayPreparewife());
console.log(deng.Preparewife)//直接看看不到  即私有化变量

F 变成了私有化变量

var inherit = (function(){
    var F = function(){};//F放在闭包,私有化变量
    return function(Target,Origin){
        F.prototype = Origin.prototype;
        Target.prototype = new F();
        Target.prototype.constuctor = Target;
        Target.prototype.uber = Origin.prototype;
    }
}());

离散数学(重要)——>CS 专业(计算机专业),,,,概率论,人工智能

命名空间

就是对象

管理变量,防止污染全局,适用于模块化开发

一个页面,多人协作,合并冲突

1.老办法:命名空间

var org = {
    department1 : {
        jicheng : {
            name : "abc";
            age : 123;
        },
        xuming : {

        }
    },
    department2 : {
        zhangsan : {

        },
        lisi : {

        }
    }
}
var jicheng = org.department1.jicheng;
jicheng.name;

2.新方法:闭包私有化变量

webpack

闭包(实现变量私有化)+立即执行函数

var name = "bac";
var init = (function(){
    var name = "abc";
    function callName(){
        console.log(name);
    }
    return function(){
        callName();
    }
}())
init();

协作也不冲突

var name = "bac";
var init = (function(){
    var name = "abc";
    function callName(){
        console.log(name);
    }
    return function(){
        callName();
    }
}())
var initDeng = (function(){
    var name = 123;
    function callName(){
        console.log(name);
    }
    return function(){
        callName();
    }
}())

如何实现链式调用模式(模仿 jquery)
obj.eat().smoke().drink().eat().sleep();

$('div').css('background-color','red').width(100).height(100).
html(123).css('position','absolute').
css('left','100px').css('top','100px');

模拟 jQuery 实现连续调用方法

var deng = {
    smoke : function () {
        console.log('Smoking...xuan cool!!!');
    },
    drink : function () {
        console.log('Drinking...good');
    },
    perm : function(){
        console.log('perming...cool');
    }
}
deng.smoke();
deng.drink();
deng.perm();//怎么实现JQ一样连续调用

为什么不能连续调用

console.log('Smoking...xuan cool!!!');
// return undefined;

方法:

var deng = {
    smoke : function () {
        console.log('Smoking...xuan cool!!!');
        return this;
    },
    drink : function () {
        console.log('Drinking...good');
        return this;
    },
    perm : function(){
        console.log('perming...cool');
        return this;
    }
}
deng.smoke().drink().perm().smoke().drink();

查看属性

想实现 num 为几,就叫哪个媳妇

var deng = {
    wife1 : {name : "xiaoliu"},
    wife2 : {name : "xiaozhang"},
    wife3 : {name : "xiaowang"},
    wife4 : {name : "xiaoli"},
    sayWife : function(num) {
        switch(num) {
            case 1:
                return this.wife1;
        }
    }
}

把上述代码简化:变量拼接属性名

var obj = {
    name : "abc"
}

**obj.name—->obj[‘name’];**内部会转换成方括号,so 这样写也对
方括号可以字符串拼接
功能实现

var deng = {
  wife1: { name: "xiaoliu" },
  wife2: { name: "xiaozhang" },
  wife3: { name: "xiaowang" },
  wife4: { name: "xiaoli" },
  sayWife: function (num) {
    switch (num) {
      case 1:
        return this["wife" + num]; //字符串加啥都是字符串
    }
  },
};
console.log(deng.sayWife(1));

对象的枚举

for in 循环

数据组的遍历:想知道十个人的信息,挨个知道

var arr = [1,3,3,4,5,6,7,8,9];
//遍历 枚举 enumeration
for(var i = 0; i < arr.length; i++){
    console.log(arr[i]);//遍历
}

如果想查找(遍历)对象的呢(不知道啥类型,编辑器角度)

var obj = {
    name : "123",
    age : 123,
    sex : "male",
    height : 180,
    weight : 75
    //prop:123
}
for(var prop in obj){
    console.log(obj.prop);//不好使---obj.prop--->obj['prop']当成属性了
}

正解:

var obj = {
    name : '13',
    age : 123,
    sex : "male",
    height : 180,
    weight : 178,
    __proto__ : {
        lastName : "deng"//也会把原形东西拿出来,不想把原形拿出来————hasOwnProperty
    }
}
for(var prop in obj) {
    console.log(obj[prop]);
    //console.log(obj['prop']);//跟obj.prop一样,这样也不对
}

实现 obj1 里面属性+1 返回
错误示范

var obj1 = {
    a : 123,
    b : 234,
    c : 456
}
var key;
for(key in obj1) {//这样写也行
    obj1.key ++;
}

1.hasOwnProperty(重点)

过滤性方法

判断这个属性是否是自己的(不是原形链的)
true
false

不想把原形拿出来 demo

var obj = {
    name : '13',
    age : 123,
    sex : "male",
    height : 180,
    weight : 178,
    __proto__ : {
        lastName : "deng"
    }
}
for(var prop in obj) {
    if(obj.hasOwnProperty(prop)){
        console.log(obj[prop]);
    }
}

自己设的一定能打印,系统设的一定不能

var obj = {
    name : '13',
    age : 123,
    sex : "male",
    height : 180,
    weight : 178,
    __proto__ : {
        lastName : "deng",
        __proto__ : Object.prototype//一旦延展的原型链的最顶端,不会打印最顶端
    }
}

2.in

和 hasOwnProperty 类似

var obj = {
    name : '13',
    age : 123,
    sex : "male",
    height : 180,
    weight : 178,
    __proto__ : {
        lastName : "deng"
    }
}
//console.log(height in obj)//height叫变量
console.log('height' in obj)//height这个属性名存不存在于obj
concole.log('lastName' in obj)

和 hasOwnProperty 的区别:
In 判断能不能访问到这个属性或者原形(只能判断对象上能不能访问到这个属性,也可以在父级找)
hasOwnProperty 判断属性属不属于这个对象
开发从来不用

3.instanceof(重点)

function Person(){
}
var person = new Person();
var obj = {};
// A对象是不是B构造函数构造出来的
console.log(person instanceof Person);
// ---->ture

看 A 对象的的原型链上有么有 B 的原形(重点)

console.log(person instanceof Object);
---->ture
console.log([] instanceof Array);
---->ture
console.log([] instanceof Object);
---->ture
console.log(person instanceof Array);
---->false
console.log(obj instanceof Person);
---->false //两个没关系

解决的问题

判断变量是数组还是对象(机器识别)

typeof([])
"object"
typeof({})
"object"
//需求:
var arr = [] || {};

第一种方法:

var obj = {};//var obj = [];
obj.constructor//直接区分出来了

第二种方法:

[] instanceof Array	 //true
var obj = {};
obj instanceof Array //false

第三种方法:toString

Object.prototype.toString.call([]);
//Object.prototype.toString = function (){
//谁调用,this就是谁
//识别this:
//返回相应结果
//}
// obj.toString();
// call.toString();
Object.prototype.toString.call([]);//数组会替换this
Object.prototype.toString.call(123);
Object.prototype.toString.call({});//区别数组和对象

控制台验证

Object.prototype.toString.call([]);
"[object Array]"
Object.prototype.toString.call({})
"[object Object]"
Object.prototype.toString.call(123);
"[object Number]"

this

1.函数预编译过程 this —> window

function test(c) {
    //var this = Object.create(test.prototype);
    //相当于
    // {
    // 	__proto__ : test.prototype
    // }
    var a = 123;
    function b(){}
}
//AO{
//  arguments : [1],//自带的
//  this : window,//自带的
//  c : 1,
//  a : undefined,
//  b : function() {}
//}
test(1);
new test();//new导致this指向发生了改变,不在是window了

验证

function test(){
    console.log(this);
}
test();

2.全局作用域里 this —> window

console.log(window);

3.call/apply 可以改变函数运行时 this 指向

4.obj.f(); f()里面的 this 指向 obj)

谁调用的方法,this 就指向谁,如果没人调用这个方法,空执行,this 是 window

var obj = {
    a : function () {
        console.log(this.name)//谁调用,就用谁的name
    },
    name : 'abc'
}
obj.a();//obj调用

5.this 题

条件判断为假的情况有: 0 、false 、’’ 、 null 、undefined 、未定义对象。
函数声明写在运算符中,其为 true,但 放在运算符中的函数声明在执行阶段时找不到的 。
另外,对未声明的变量执行 typeof 不会报错,会返回 undefined

this 一马平川题

var name = "222";
var a = {
    name : "111",
    say : function(){
        console.log(this.name);
    }
}
var fun = a.say;//a.say代表function的函数引用,fun在全局上执行,相当于函数在全局执行
fun()//222全局执行也没人调用
a.say()//111
var b = {
    name : "333",
    say : function(fun){
        //this---->b
        fun();//不是this.fun(),谁也没调用,预编译,上面得this--->window,222
    }
}
b.say(a.say);//a.say---是上面的函数体,       222
b.say = a.say;//a.say上面的函数拷贝到b.say(代替b.say)
b.say();//333

arguments

arguments.callee

func.caller

arguments

function test(){
 	console.log(arguments.callee);
 }
test();//返回自己的函数体

应用:

var num = (function(n){
    if(n == 1) {
        return 1;
    }
    return n*阶乘(n-1);
}(100))

立即执行函数的阶乘:只能用 callee 做

var num = (function(n){
    if(n == 1) {
        return 1;
    }
    return n*arguments.callee(n-1);
}(100))

在那个函数里面,就打印哪个

function test(){
    console.log(arguments.callee);
    function demo(){
        console.log(arguments.callee);
    }
    demo();
}

caller: demo 在哪个环境调用的

function test () {
	demo();
}
function demo() {
    console.log(demo.caller);
}
test();

克隆

1.浅层克隆

var obj = {
    name : 'abc',
    age : 123,
    sex : 'female'
}
var obj1 = {}
function clone(origin,target){
    for(var prop in origin){
        target[prop] = origin[prop];
    }
}
clone(obj,obj1);
//打印obj1显示已经copy过去了

实现容错:

var obj = {
    name : 'abc',
    age : 123,
    sex : 'female'
}
var obj1 = {}
function clone(origin,target){
    var target = target || {};
    for(var prop in origin){
        target[prop] = origin[prop];
    }
    return target;
}
clone(obj,obj1);

原始值没问题,但是引用值拷贝:

var obj = {
  name: "abc",
  age: 123,
  sex: "female",
  card: ["visa", "unionpay"],
};
var obj1 = {};
function clone(origin, target) {
  var target = target || {};
  for (var prop in origin) {
    target[prop] = origin[prop];
  }
  return target;
}
clone(obj, obj1);

2.深层克隆

两个人,克隆,只考虑引用值的数组、对象 核心:区分数组和对象

var obj = {
    name : 'abc',
    age : 123,
    sex : 'female',
    card : ['visa','unionpay',[1,2]]
}
var obj1 = {
    card : [obj.card[0],obj.card[1],[]]
}
clone(obj, obj1);
var obj = {
    name : 'abc',
    age : 123,
    card : ['visa', 'master'],
    wife : {
        name : "bcd",
        son : {
            name : "aaa"
        }
    }
}
var obj1 = {
    name : obj.name,
    age : 123,
    card : []//重新开始循环,发现里面都是原始值,card : [obj.card[0],obj.card[1]],
    wife : {
    name : "bcd",
    son : {
    name : "bcd",
    son :
}
}//里面是不是原始值,第一个是,第二个不是, 建立新的对象,进行循环
}

遍历对象 for(var prop in obj)
遍历除了可以遍历对象,还可以遍历数组

var arr = ['a','b','c']
for(var prop in arr) {
    arr[prop]
}

步骤:

1.判断是不是原始值 typeof()如果是 object 引用值,不是 obj,基本是原始值 null 最后讲 2.数组还是对象 三种方法: instanceof toString constructor,建议使用 toString,因为
另外两个有小问题,不会遇到
父子域:一个页面里面可能还有个子页面 跨父子域 [] instanceof Array ——>false 应该 ture 3.建立相应的数组和对象

// 递归
function deepClone(origin,target) {
    var target = target || {};//容错
    toStr = Object.prototype.toString,
        arrStr = "[object Array]";//比对
}

代码实现

var obj = {
    name : 'abc',
    age : 123,
    card : ['visa', 'master'],
    wife : {
        name : "bcd",
        son : {
            name : "aaa"
        }
    }
}
var obj1 = {

}

function deepClone(origin,target) {
    var target = target || {};//容错
    toStr = Object.prototype.toString,
        arrStr = "[object Array]";
    for(var prop in origin) {
        if(origin.hasOwnProperty(prop)) {//防止原型链上的
            if(typeof(origin[prop]) == 'object') {
                if(toStr.call(origin[prop]) == arrStr) {
                    target[prop] = [];
                }else{
                    target[prop] = {};
                }
                deepClone(origin[prop],target[prop]);//递归
            }else{
                target[prop] = origin[prop];
            }
        }
    }
    return target;
}

优化

function print(){
    function deepClone(origin, target){
        var target = target || {},
            toStr = Object.prototype.toString,
            arrStr = "[object Array]";
        for(var prop in origin) {
            if(origin.hasOwnProperty(prop)) {//null问题
                if(origin[prop] !== "null" && typeof(origin[prop]) == 'object') {
                    //绝对不等于,隐式类型转换也不行
                    if(toStr.call(origin[prop]) == arrStr) {
                        target[prop] == [];
                    }else{
                        target[prop] = {};
                    }
                    deepClone(origin[prop],target[prop]);
                }else{
                    target[prop] = origin[prop];
                }
            }
        }
        return target;
    }

三目运算符简化代码

function print(){
    function deepClone(origin, target){
        var target = target || {},
            toStr = Object.prototype.toString,
            arrStr = "[object Array]";
        for(var prop in origin) {
            if(origin.hasOwnProperty(prop)) {//null问题
                if(origin[prop] !== "null" && typeof(origin[prop]) == 'object') {
                    //绝对不等于,隐式类型转换也不行

                    target[prop] = (toStr.call(origin[prop]) == arrStr) ? [] : {};
                    deepClone(origin[prop],target[prop]);
                }else{
                    target[prop] = origin[prop];
                }
            }
        }
        return target;
    }

数组

数组的定义

区分对象的定义方式
(1) 自面量
(2) 构造函数——系统自带
(3) 自定义构造函数
(4) Var Obect.create()

数组 1.数组字面量 var arr = [];

var arr = [1,2,3,,,5];//稀松数组

2.new Array(length/content);

构造方法   var arr = new Array(1,2,3,4,5);

区别

var arr = new Array(10);//返回长度为10的稀松数组
var arr1 = [10];
var arr = new Array(10.2);//长度为10.2 报错

数组的读和写

arr[num] //不可以溢出读   结果undefined
arr[num] = xxx;//可以溢出写,撑长数组

JS 中,数组就算没有第 10 位也不报错,因为数组是基于对象的
数组常用的方法

1.改变原数组 7 个

reverse,sort,push,pop,shift,unshift,

push 把数组的最后一位增加

封装 push()

var arr = [1,2,3];
Array.prototype.push = function (){//不能写形参,因为不知道有几个,所以只能用arguments
    for(var i = 0; i < arguments.length; i++) {
        this[this.length] = arguments[i];
    }
    return this.length;
}

pop 删除:把数组的最后一位剪切出去

arr.pop(); 不能传参

shift:把前面减 arr.shift()

unshift:和 push 方向相反,在前面加东西

封装 unshift

数组不能向前面放东西,所以用新数组,在拼接起来(concat)

reverse:逆反

sort

arr.sort();//升序排序

arr.sort().reverse()//降序

但是这个排序是按 ASCII 排的,so
var arr = [1,2,10,2,4,5];不能实现想要的排序
实现排序:(冒泡排序) 1.必须两个形参 2.看返回值

(1)返回值为负数,前面的数放在前面
(2)为正数,后面的数在前
(3)为 0,不动

var arr = [1,2,10,2,4,5];
arr.sort(function (a, b) {
    if(a > b) {
        return 1;
    }else{
        return -1;
    }
});

简化代码

arr.sort(function (a, b) {
    if(a - b > 0) {
        return a - b;
    }else{
        return a - b;
    }
});

最终

arr.sort(function (a, b) {
    return a - b;//升序
    //return b - a;//降序
});

给一个有序数组乱序

Math.random()    返回(0,1)随机数
// var arr = [1,2,3,4,5,6,7];
// arr.sort(function () {
// 	return Math.random() - 0.5;//实现可正可负
// });

对象年龄排序

var cheng = {
    name : "cheng",
    age : 18,
    sex : 'male',
    face : "handsome"
}
var deng = {
    name : "deng",
    age : 40,
    sex : undefined,
    face : "amazing"
}
var zhang = {
    name = "zhang",
    age = 20,
    sex = "male"
}
var arr = [cheng, deng, zhang];
arr.sort(function (a, b) {
    // if(a.age > b.age) {
    // 	return 1;
    // }else{
    // 	return -1;
    // }
    return a.age - b.age;
}

字符串长度排序

var arr = ['ac','bcd','cccc','asfsadshilk','casuicbniasbnciuas'];
arr.sort(function (a, b){
    return a.length - b.length;
})

字节长度排序

function retBytes(str) {
    var num = str.length;
    for(var i = 0; i < str.length; i++){
        if(str.charCodeAt(i) > 255){
            num ++;
        }
    }
    return num;
}
var arr = ['a邓','ba邓','cc邓cc','老邓',"残邓",'asdoifqwoeiur','asdf'];
arr.sort(function (a, b){
    return retBytes(a)-retBytes(b);
})

splice:切片

// arr.splice(从第几位开始,剪切多少长度,在切口处添加新的数据)
arr.splice(1,2);
demo
var arr = [1,1,2,2,3,3];
arr.splice(1,1,0,0,0);


var arr = [1,2,3,5];//实现把4填进去
arr.splice(3,0,4);//鼠标光标在前面


arr.splice(-1,1);//倒数第一位

-1 倒数第一位;1 倒数 第二位怎么实现的

splice = function (pos) {
  pos += pos > 0 ? 0 : this.length; //负数
};

2.不改变原数组

concat,join—>split,toString

  1. concat 连接
var arr = [1,2,3,4,5,6];
var arr1 = [7,8,9];
arr.concat(arr1);

toString

把数组变成字符串

Slice 截取

var arr = [1,2,3,4,5,6];
// 1.两个参数,slice(从该位开始截取,截取到该位)
// var newArr = arr.slice(1,3);
// 2.一个参数slice(从第几位开始截取,截取到最后)
var newArr = arr.slice(1)
var newArr = arr.slice(-4)//-4+6位
// 3.没参数:全截取

join

实现字符串连接

var arr = [1,2,3,4];
arr.join("-")//必须是字符串形式
arr = [1-2-3-4];

split()互逆方法:按照什么拆分

var arr = [1-2-3-4];
arr.split("3")//必须是字符串形式

类数组

是对象,可以当数组一样用

类数组 1.可以利用属性名模拟数组的特性 2.可以动态的增长 length 属性 3.如果强行让类数组调用 push 方法,则会根据 length 属性值的位置进行属性的扩充。

function test() {
    console.log(arguments);
    arguments.push(7);//报错
}
test(1,2,3,4,5,6);

现象

//完成类数组的基本形态构建
var obj = {
    "0" : 'a',
    "1" : 'b',
    "2" : 'c',
    "length" : 3,
    "push" : Array.prototype.push
}
obj.push('d');
//导致length=4并且"3":d

类数组:是对象,可以当数组一样用

var obj = {
  0: "a",
  1: "b",
  2: "c",
  length: 3,
  push: Array.prototype.push,
  splice: Array.prototype.splice, //加上他就变成可以当数组用了,像数组了
};
//类数组组成部分:属性要为索引(数字)属性,必须有length属性,最好加上push

length 内部操作手法:

Array.prototype.push = function(target) {
    this[this.length] = target;
    this.length ++;
}
//如果对象调用,则
Array.prototype.push = function(target) {
    obj[obj.length] = target;
    obj.length ++;
}

类数组所有元素遍历出来

var obj = {
    "0" : "a",
    "1" : "b",
    "2" : "c",
    name : "abc",
    age : 123,
    length : 3,
    push : Array.prototype.push,
    splice : Array.prototype.splice
}
for(var prop in obj){
    console.log(obj[prop])
}

封装 type

typeof([])– array
typeof({})– object
typeof(function)– function
typeof(new Number())– new Object
typeof(123)– number
分两类:1、原始值 引用值 2、区分引用值

function type(target) {
    var template = {
        "[object Array]" : "array",
        "[object Object]" : "object",
        "[object Number]" : "number - object",
        "[object Boolean]" : 'boolean - object',
        "[object String]" : 'string - object'
    }
    if(target === null){
        return "null";
    }
    // if(typeof(target) == 'function') {
    // 	return 'function';
    // }else if(typeof(target) == "object") = {
    if(typeof(target) == "object"){
        // 数组;
        // 对象;
        // 包装类Object.prototype.toString
        var str = Object.prototype.toString.call(target);
        return template[str];
    }else{//原始值
        return typeof(target);
    }

数组去重,在原型链上编程 ​ hash 哈西

var arr = [1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 0, 0];
Array.prototype.unique = function () {
  var temp = {},
    arr = [],
    len = this.length; //也是优化,不用每次都this了
  for (var i = 0; i < len; i++) {
    if (!temp[this[i]]) {
      //如果有0,!0==true,所以还是"abc"吧
      temp[this[i]] = "abc";
      arr.push(this[i]);
    }
  }
  return arr;
};

三目运算符

形式:判断语句? 若为真,执行,并返回结果:若为假,执行,并返回结果
三目运算符其实就是简化版的 if(){…}else{}语句
条件判断?是 :否 并且会返回值

var num = 1 > 0 ? 2 + 2 : 1 + 1;
var num = 1 > 0 ? ("10" > "9" ? 1 : 0) : 2;

try…catch

try{}catch(e) {}一行代码报错影响后面代码执行

finally{}

//try...catch
try {
  console.log("a");
  console.log(b);
  console.log("c");
} catch (e) {}
console.log("d");
//执行到b就不执行了,在try里面发生的错误,不会执行错误后的try里面的代码,打印ad

关于 catch

try{
    console.log('a');
    console.log(b);
    console.log('c');
}catch(e){//error  error.message  error.name ---> error
    console.log(e.name + " : " + e.message);
}
console.log('d');

小问题

try{
    console.log('a');
    console.log(b);
    console.log(c);//虽然错误,但是不执行
}catch(e){
    console.log(e.name + " : " + e.message);
}
console.log('d');

Error.name 的六种值对应的信息:
1.EvalError:eval()的使用与定义不一致
2.RangeError:数值越界
3.ReferenceError:非法或不能识别的引用数值
4.SyntaxError:发生语法解析错误
5.TypeError:操作数类型错误
6.URIError:URI 处理函数使用不当

es5 严格模式

如今:基于 es3.0+es5.0 的新增方法 使用的,如果产生冲突,则遵循 es3.0 的
要讲的是怎么让他遵循 es5.0 解决,即 es5.0 的严格模式,则产生冲突的部分用 es5.0,否则 es3.0
“use strict”
不再兼容 es3 的一些不规则语法。使用全新的 es5 规范。
demo1:es5 不允许使用 arguments.callee

// es5.0严格模式的启动
"use strict"//放在逻辑的最顶端
function test() {
    console.log(arguments.callee);
}
test();

demo2

function demo() {
  console.log(arguments.callee);
}
demo(); //es3.0
function test() {
  "use strict"; //内部es5.0
  console.log(arguments.callee);
}
test();

两种用法:
全局严格模式
局部函数内严格模式(推荐)
就是一行字符串,不会对不兼容严格模式的浏览器产生影响
为什么用字符串”use strict”:可能在老浏览器执行,浏览器版本问题——向后兼容
es5 不允许用 with(){} with 可以改变作用域链,with(obj) obj 作为最顶端的 AO

var obj = {
    name : "obj"
}
var name = 'window';
function test() {
    var name = 'scope';
    with(obj) {//如果with里面添加了对象,with(obj),会把对象当做with要执行的代码体的作用域链的最顶端
        console.log(name);
    }
}
test();

演示

var obj = {
    name:"obj",
    age:234
}
var name = "window";
function test(){
    var age = 123;
    var name = "scope";
    with(obj){
        console.log(name);//obj
        console.log(age);//234
    }
}
test();

with 作用:简化代码

var org = {
    dp1 : {
        jc : {
            name : 'abc',
            age : 123
        },
        deng : {
            name : "xiaodneg",
            age : 234
        }
    },
    dp2 : {

    }
}
with(org.dp1.jc) {//直接访问org.dp1.jc里面的
    console.log(name);
}

应用:document 也是对象

document{
    write : function () {}
}
with(document) {
    write('a');
}

不支持 with,arguments.callee,func.caller,变量赋值前必须声明,局部 this 必须被赋值,(Person.call(null/undefined) 赋值什么就是什么),拒绝重复属性和参数

"use strict"
function test(){
    console.log(this);//undefined
}
test();

new 了

"use strict"
function Test(){
    console.log(this);//控制台显示constructor的名
}
new Test();

预编译在 es5 严格模式下 this 不在指向 window,没有指向(空),this 必须被赋值,赋值什么就是什么

"use strict"
function Test(){
    console.log(this);
}
Test.call({});

赋值 123

"use strict"
function Test(){
    console.log(this);
}
Test.call(123);

123 是原始值:ES3 就会变成包装类

function Test(){
    console.log(this);
}
Test.call(123);

严格模式,在全局,this 指向 window

"use strict"
console.log(this);

es5 拒绝重复属性和参数。。es3 里面重复属性和参数是不报错的

function test (name , name){
    console.log(name);
}
//test(1,2);
//test(2);

参数报错

"use strict"
function test (name , name){
    console.log(name);
}
test(1,2);

属性不报错

"use strict"
var obj={
    name:'112',
    name:'111'
}
test(1,2);

关于 eval:

  1. es3 不能用 eval();——能改变作用域

  2. eval 能执行字符串

"use strict";
var a = 123;
eval('console.log(a)');//字符串在eval里面执行

面试题:为什么不用 with();

with 可以改变作用域链,改变作用域链,都会降低效率


文章作者: Sunny
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Sunny !
  目录