js学习笔记


张登友,张登友的博客,张登友的网站——

JavaScript笔记

数据分为

  • 静态数据(指一些永久性的数据)
  • 动态数据(程序运行过程中,动态产生的临时数据,一般存储在内存中)

JS 中一共有八种数据类型

  • 基本数据类型(值类型):String 字符串、Number 数值、BigInt 大型数值、Boolean 布尔值、Null 空值、Undefined 未定义、Symbol。

  • 引用数据类型(引用类型):Object 对象。

面试问:引用数据类型有几种?

面试答:只有一种,即 Object 类型。

数据类型之间最大的区别:

  • 基本数据类型:参数赋值的时候,传数值。

  • 引用数据类型:参数赋值的时候,传地址。

栈内存和堆内存

  • JS 中,所有的变量都是保存在栈内存中的。

    基本数据类型的值,直接保存在栈内存中。值与值之间是独立存在,修改一个变量不会影响其他的变量。

  • 引用数据类型

    对象是保存到堆内存中的。每创建一个新的对象,就会在堆内存中开辟出一个新的空间;而变量保存了对象的内存地址(对象的引用),保存在栈内存当中。如果两个变量保存了同一个对象的引用,当一个通过一个变量修改属性时,另一个也会受到影响。

模板字符串中插入变量

var name = 'qianguyihao';
var age = '26';
console.log('我是' + name + ',age:' + age); //传统写法
console.log(`我是${name},age:${age}`); //ES6 写法。符号是反引号(在 tab 键的上方)

模板字符串中插入表达式

const a = 5;
const b = 10;

console.log('this is ' + (a + b) + ' and\nnot ' + (2 * a + b) + '.'); // 传统写法需要添加转义字符换行

// 下面这行代码,故意做了换行。
console.log(`this is ${a + b} and
not ${2 * a + b}.`);

模板字符串可以换行

const result = {
    name: 'zdy',
    age: 25,
    sex: '男',
};
// 模板字符串支持换行
const html = `<div>
<span>${result.name}</span>
<span>${result.age}</span>
<span>${result.sex}</span>
</div>`;

console.log(html); // 打印结果也会换行

模板字符串中可以调用函数

console.log(html); // 打印结果也会换行

function getName() {
return 'zdynb';
}

console.log(`www.${getName()}.cn`); // 打印结果:www.zdynb.cn

模板字符串支持嵌套使用

const nameList = ['刺客', '伍六七', '哈哈哈'];

function myTemplate() {
    // join('') 的意思是,把数组里的内容合并成一个字符串
    return `<ul>
	${nameList.map((item) => `<li>${item}</li>`).join('')}
	</ul>`;
}
document.body.innerHTML = myTemplate();

注:map() 方法创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。示例:

const array1 = [1, 4, 9, 16];

// pass a function to map
const map1 = array1.map(x => x * 2);

console.log(map1);
// expected output: Array [2, 8, 18, 32]

十、数值范围

  • 最大值:Number.MAX_VALUE,这个值为: 1.7976931348623157e+308
  • 最小值:Number.MIN_VALUE,这个值为: 5e-324

如果使用 Number 表示的变量超过了最大值,则会返回 Infinity。

  • 无穷大(正无穷):Infinity

  • 无穷小(负无穷):-Infinity

NAN

  • typeof NaN的返回结果是 number。

  • Undefined 和任何数值计算的结果为 NaN。NaN 与任何值都不相等,包括 NaN 本身。

连字符和加号的区别

  • 如果加号两边都是 Number 类型是数字相加。
  • 否则,就是连字符(用来连接字符串)。

隐式转换

-*/%这几个符号会自动进行隐式转换。

"2"+1得到的结果其实是字符串,但是"2"-1得到的结果却是数值 1,这是因为计算机自动帮我们进行了“隐式转换

浮点数运算精度

  • 整数的运算基本可以保证精确
  • 小数的运算,可能会得到一个不精确的结果
var a = 0.1 + 0.2;
console.log(a); //打印结果十分意外:0.30000000000000004
  1. 简单的精度问题:可以使用 toFix() 方法进行小数的截取。

  2. 数学运算的开源库

  • decimal.js:属于很全面的运算库,文件很大,压缩后的文件就有 500kb。如果你的项目涉及到大型的复杂运算,可以使用 Math.js。
  • Math.js:属于轻量的运算库,压缩后的文件只有 32kb。大多数项目的数学运算,使用 decimal.js 足够了。

Null:空对象

  • Null 类型的值只有一个,就是 null。比如 let a = null

  • 使用 typeof 检查一个 null 值时,会返回 object。null 虽然是一个单独的数据类型,但null 相当于是一个 object,只不过地址为空(空指针)而已

undefined:未定义类型

  1. 变量已声明但未赋值时,此时它的值就是 undefined
  2. 变量未声明(未定义)时,此时,如果用 typeof 检查这个变量时,会返回 undefined
  3. 函数无返回值时,那么这个函数的返回值就是 undefined。也可以这样理解:在定义一个函数时,如果末尾没有 return 语句,那么,其实就是 return undefined
  4. 调用函数时未传参,那么这个参数的值就是 undefined。

null 和 undefined 的区别

  • null 和 undefined 有很大的相似性。看看 null == undefined 的结果为 true 也更加能说明这点。
  • 但是 null === undefined 的结果是 false。它们虽然相似,但还是有区别的,其中一个区别是,和数字运算时:
    • 10 + null 结果为 10。
    • 10 + undefined 结果为 NaN。

规律:

  • 任何值和 null 运算,null 可看做 0 运算。
  • 任何数据类型和 undefined 运算都是 NaN。

typeof 运算符

typeof 的写法 返回结果
typeof 数字 number
typeof 字符串 string
typeof 布尔型 boolean
typeof 对象 object
typeof 方法 function
typeof null object
typeof undefined undefined
typeof NaN number
  • 因为 null 代表的是空对象
  • 空数组[]、空对象{} 在使用 typeof 时,返回值也是 object。因为空数组、空对象都是引用数据类型 Object
  • typeof 无法区分数组,但 instanceof 可以。

变量的类型转换的分类

类型转换分为两种:显式类型转换、隐式类型转换。

显示类型转换
  • toString()

  • String()

  • Number()

  • parseInt(string)

  • parseFloat(string)

  • Boolean()

隐式类型转换
  • isNaN ():任何不能被转换为数值的参数,都会让这个函数返回 true

    • 执行过程

      (1)先调用Number(参数)函数;

      (2)然后将Number(参数)的返回结果是否为数值。如果不为数值,则最终结果为 true;如果为数值,则最终结果为 false。

  • 自增/自减运算符:++—-

    • 执行过程:

      (1)先调用Number(参数)函数;

      (2)然后将Number(参数)的返回结果进行 加 1 操作。

  • 正号/负号:+a-a

    • 任何值做+a-a运算时, 内部调用的是 Number() 函数。不会改变原数值(这里说的是正号/负号,不是加号/减号)
  • 加号:+

    • 变量+”” 或者 变量+”abc”(此处为连接符,非加号)
  • 运算符:-*/%

    • 运算时,会将这些值转换为 Number 然后再运算(内部调用的是 Number() 函数)
隐式类型转换(特殊)
  • 逻辑运算符:&&||! 。非布尔值进行与或运算时,会先将其转换为布尔值,然后再运算。&&||的运算结果是原值!的运算结果为布尔值。
  • 关系运算符:<> <= >=等。关系运算符,得到的运算结果都是布尔值:要么是 true,要么是 false。

其他简单类型—->String

  • 隐式类型转换:字符串拼接

    格式:变量+”” 或者 变量+”abc”(实际上内部是调用的 String() 函数)

  • 调用 toString()方法,格式:

    • 变量.toString();
    • var result = 变量.toString();
    • null 和 undefined 这两个值没有 toString() 方法,所以不能用 toString() 。如果调用,会报错。

    【重要】该方法不会影响到原变量,它会将转换的结果返回。当然我们还可以直接写成a = a.toString(),这样的话,就是直接修改原变量。

  • 使用String()函数,格式:

    • String(变量);

    • 使用 String()函数做强制类型转换时:

      • 对于 Number、Boolean、Object 而言,本质上就是调用 toString()方法。
  • prompt():用户的输入

    prompt()是用来弹出能够让用户输入的对话框。用户不管输入什么,都当字符串处理。

其他的数据类型 —-> Number

  1. 使用Number()函数

    • 字符串—>数字

      • 如果字符串是一个空串或者是一个全是空格的字符串,则转换为 0

      • 只要字符串中包含了其他非数字的内容(小数点按数字来算),则转换为 NaN。使用 Number() 函数之后,如果无法转换为数字,就会转换为 NaN

      • 布尔—>数字

        • true转成1
        • false转成0
    • null—>数组,结果为0

    • undefined—>数字,结果为NAN

  2. 隐式类型转换:正负号 +a-a

    注意,这里说的是正号/负号,不是加号/减号。

    任何值做+a-a运算时, 内部调用的是 Number() 函数。不会改变原数值。

  3. 使用 parseInt()函数:字符串 -> 整数

  • parseInt()**:将传入的数据当作字符串**来处理,从左至右提取数值, 一旦遇到非数值就立即停止;停止时如何还没有提取到数值, 那么就返回NaN。

  • 转换情况有以下几种:

    1. 字符串 –> 数字

      (1)只保留字符串最开头的数字,后面的中文自动消失

      (2)如果字符串不是以数字开头,则转换为 NaN

      (3)如果字符串是一个空串或者是一个全是空格的字符串,转换时会报错

    2. Boolean –> 数字,结果为:NaN

    3. Null –> 数字,结果为:NaN

    4. Undefined –> 数字,结果为:NaN

  • Number(true)parseInt(true)/parseFloat(true) 的区别:

    • Number() :转换为数字;如果转换不了则返回 NaN。

    • parseInt()/parseFloat() :提取出最前面的数字部分;没提取出来,那就返回 NaN。

  • parseInt()、parseFloat()会将传入的数据当作字符串来处理。也就是说,如果对非 String使用 parseInt()、parseFloat(),会先将其转换为 String 然后再操作。【重要】

  1. parseFloat()函数:字符串 –> 浮点数(小数)

    parseFloat()的作用是:将字符串转换为浮点数

转换为 Boolean

  1. 数字 –> 布尔。 0 和 NaN是 false,其余的都是 true。比如 Boolean(NaN)的结果是 false。

  2. 字符串 —> 布尔。空串是false,其余的都是 true。全是空格的字符串,转换结果也是 true。字符串'0'的转换结果也是 true。

  3. null 和 undefined 都会转换为 false。

  4. 引用数据类型会转换为 true。空数组[]和空对象{}转换结果也是 true

    const result1 = '';
    const result2 = { a: 'data1', b: 'data2' };
    
    if (result1) {
        console.log('result1数值为空,不执行');
    }
    
    if (result2 && result2.a) {
        // 接口返回了 result2,且 result2.a 里面有值
        console.log('result2数据不为空,可以执行');
    }

运算符分类

算数运算符
  • 运算符 描述
    + 加、字符串连接
    -
    *
    /
    % 获取余数(取余、取模)

    假设用户输入 345,怎么分别得到 3、4、5 这三个数呢?

    // 百分号:取余。只关心余数。
    // 得到3的方法:345 除以100,得到3.45然后取整,得到3。即:parseInt(345/100)
    
    // 得到4的方法:345 除以100,余数是45,除以10,得到4.5,取整。即:
    parseInt(345 % 100 / 10)
    
    // 得到5的方法:345 除以10,余数就是5。即:
    parseInt(345 % 10)
  • 取模(取余)运算

    • 如果 n < 0,那就先把 n 取绝对值后,再计算。等价于 m % (-n)。
    • 如果 n 是 0,那么结果是 NaN。
    • 在 n > 0 的情况下:
    • 如果 m>=n,那就正常取余。
    • 如果 m<n,那结果就是 m。
    • 取余运算结果的正负性,取决于 m,而不是 n。比如:10 % -3的运算结果是 1。-10 % 3的运算结果是-1。
自增/自减运算符
  • a++ 先把 a 的值赋值给表达式,然后 a 再自增
  • ++a a 先自增,然后再把自增后的值赋值给表达式

一元运算符

只需要一个操作数(正号 +,负号 -可以对一个其他的数据类型使用+,来将其转换为 number)

var a = '123';
console.log(typeof a);

var a = true;
a = +a; //一元运算符
逻辑运算符

(注意:能参与逻辑运算的,都是布尔值)

  • && 与(且):两个都为真,结果才为真。

  • || 或:只要有一个是真,结果就是真。

  • ! 非:对一个布尔值进行取反。

赋值运算符
  • = 直接赋值。比如 var a = 5。意思是:把 5 赋值给 a。

  • +=。a += 5 等价于 a = a + 5

  • -=。a -= 5 等价于 a = a - 5

  • *=。a * = 5 等价于 a = a*5

  • /=。a /= 5 等价于 a = a / 5

  • %=。a %= 5 等价于 a = a % 5

比较运算符
  • 大于号
  • < 小于号
  • = 大于或等于
  • <= 小于或等于
  • == 等于
  • === 全等于
  • != 不等于
  • !== 不全等于
console.log(1 > true); //false
console.log(1 >= true); //true
console.log(1 > '0'); //true

//console.log(10 > null); //true

//任何值和NaN做任何比较都是false

console.log(10 <= 'hello'); //false
console.log(true > false); //true

特殊情况:如果符号两侧的值都是字符串时,不会将其转换为数字进行比较。比较两个字符串时,比较的是字符串的Unicode 编码。【非常重要,这里是个大坑,很容易踩到】

三元运算符

也叫条件运算符

条件表达式 ? 语句1 : 语句2;

执行的流程

条件运算符在执行时,首先对条件表达式进行求值:

  • 如果该值为 true,则执行语句 1,并返回执行结果

  • 如果该值为 false,则执行语句 2,并返回执行结果

如果条件的表达式的求值结果是一个非布尔值,会将其转换为布尔值然后再运算。

运算符的优先级

优先级从高到低

  • .[]new

  • ()

  • ++--

  • !~+(单目)、-(单目)、typeofvoiddelete

  • */%

  • +(双目)、-(双目)

  • <<>>>>>

  • 关系运算符:<<=>>=

  • ==!=====!==

  • &

  • ^

  • |

  • &&

  • ||

  • ?:

  • =+=-=*=/=%=<<=>>=>>>=&=^=|=

  • ,

注意:逻辑与 && 比逻辑或 || 的优先级更高。

备注:如果不清楚哪个优先级更高,可以把括号用上。

接口的返回码 retCode的if推荐写法

直接通过 return 的方式,让 function 里的代码不再继续往下走,因为要用到 return ,所以整段代码是封装到一个 function 里的。

let retCode = 1003; // 返回码 retCode 的值可能有很多种情况
handleRetCode(retCode);

// 方法:根据接口不同的返回码,处理前端不同的显示状态
function handleRetCode(retCode) {
    if (retCode == 0) {
        alert('接口联调成功');
        return;
    }

    if (retCode == 101) {
        alert('活动不存在');
        return;
    }

    if (retCode == 103) {
        alert('活动未开始');
        return;
    }

    if (retCode == 104) {
        alert('活动已结束');
        return;
    }

    if (retCode == 1001) {
        alert('参数错误');
        return;
    }

    if (retCode == 1002) {
        alert('接口频率限制');
        return;
    }

    if (retCode == 1003) {
        alert('未登录');
        return;
    }

    if (retCode == 1004) {
        alert('(风控用户)提示 活动太火爆啦~军万马都在挤,请稍后再试');
        return;
    }

    // 其他异常返回码
    alert('系统君失联了,请稍候再试');
    return;
}

switch精简写法

适时去掉break

let day = 2;

switch (day) {
    case 1:
    case 2:
    case 3:
    case 4:
    case 5:
        console.log('work');
        break; // 在这里放一个 break

    case 6:
    case 7:
        console.log('relax');
        break; // 在这里放一个 break

    default:
        break;
}
}

for循环

for(①初始化表达式; ②条件表达式; ④更新表达式){
	③语句...
}

while循环

while(条件表达式){
	语句...
}
  
//执行流程
while语句在执行时,先对条件表达式进行求值判断:

如果值为true,则执行循环体:
	循环体执行完毕以后,继续对表达式进行判断
	如果为true,则继续执行循环体,以此类推

如果值为false,则终止循环

do…while循环

do…while语句在执行时,会先执行循环体

do{
	语句...
}while(条件表达式)

循环的label使用

outer: for (var i = 0; i < 5; i++) {
    console.log('外层循环 i 的值:' + i);
    for (var j = 0; j < 5; j++) {
        break outer; // 直接跳出outer所在的外层循环(这个outer是自定义的label)
        console.log('内层循环 j 的值:' + j);
    }
}

循环的continue

  • continue 可以用来跳过当次循环,继续下一次循环。

  • 同样,continue 默认只会离他最近的循环起作用。

  • 同样,如果需要跳过指定的当次循环,可以使用 label 标签。

for (var i = 0; i < 10; i++) {
    if (i % 2 == 0) {
        continue;
    }
    console.log('i的值:' + i);
}

对象的分类

1.内置对象:

  • 由ES标准中定义的对象,在任何的ES的实现中都可以使用

  • 比如:Object、Math、Date、String、Array、Number、Boolean、Function等。

2.宿主对象:

  • 由JS的运行环境提供的对象,目前来讲主要指由浏览器提供的对象。

  • 比如 BOM DOM。比如consoledocument

3.自定义对象:

  • 由开发人员自己创建的对象

通过 new 关键字创建出来的对象实例,都是属于对象类型,比如Object、Array、Date等。

生成【x,y】之间的随机整数

生成两个整数之间的随机整数,并且要包含这两个整数

/*
* 生成两个整数之间的随机整数,并且要包含这两个整数
*/
function getRandom(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}
console.log(getRandom(1, 10));

url 编码和解码

encodeURIComponent();   //把字符串作为 URI 组件进行编码
decodeURIComponent();   //把字符串作为 URI 组件进行解码

let url = 'http://www.zdynb.cn';
let str = encodeURIComponent(url);
console.log(decodeURIComponent(str));

打印中文的星期

var myDate = new Date();	
var dayArr  = ['星期日', '星期一', '星期二', '星期三', '星期四','星期五', '星期六'];
console.log(myDate.getDay()); // 打印结果:1
console.log(dayArr[myDate.getDay()]); // 打印结果:星期一

格式化日期

Moment.js 轻量级的JavaScript时间库

已封装的日期函数

function formatDate() {
    let date = new Date();
    let year = date.getFullYear();
    let month = date.getMonth() + 1;
    let day = date.getDate();
    let week = date.getDay();
    let weekday = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
    week=weekday[week];
    let hour = date.getHours();
    hour = hour < 10 ? '0' + hour : hour;
    let minuate = date.getMinutes()
    minuate = minuate < 10 ? '0' + hour : hour;
    second = date.getSeconds();
    second = second < 10 ? '0' + second : second;
    let now = '今天是: ' + year + '年' + month + '月' + day + '日' + ' ' + hour + ':' + minuate + ':' + second + ' '+ week;

    return now;
}

发布会倒计时

let div = document.getElementsByTagName('div')[0];

let timer = setInterval(() => {
    countDown('2022/02/03 11:20:00');
}, 1);

function countDown(myTime) {
    let nowTime = new Date();
    let future = new Date(myTime);
    let timeSum = future.getTime() - nowTime.getTime(); //获取时间差

    let day = parseInt(timeSum / 1000 / 60 / 60 / 24); // 天
    let hour = parseInt((timeSum / 1000 / 60 / 60) % 24); // 时
    let minu = parseInt((timeSum / 1000 / 60) % 60); // 分
    let sec = parseInt((timeSum / 1000) % 60); // 秒
    let millsec = parseInt(timeSum % 1000); // 毫秒

    //时间小于10补一个0
    day = day < 10 ? '0' + day : day;
    hour = hour < 10 ? '0' + hour : hour;
    minu = minu < 10 ? '0' + minu : minu;
    sec = sec < 10 ? '0' + sec : sec;
    if (millsec < 10) {
        millsec = '00' + millsec;
    } else if (millsec < 100) {
        millsec = '0' + millsec;
    }

    // 兜底处理
    if (timeSum < 0) {
        div.innerHTML = '距离发布会还有00天00小时00分00秒000毫秒';
        clearInterval(timer);
        return;
    }

    // 前端显示
    div.innerHTML = '距离发布会还有' + day + '天' + hour + '小时' + minu + '分' + sec + '秒' + millsec + '毫秒';
}

翻转数组

let arr = [10,20,30,40,50];
let newArr=[];
for (let i=0;i<arr.length;i++){
    newArr[i]=arr[arr.length-i-1]
}

冒泡排序

let arr = [30,10,20,40,60];
for (let i=0;i<arr.length;i++){
    for (let j=0;j<arr.length-i-1;j++){
        if (arr[j]>arr[j+1]){
            let temp=arr[j];
            arr[j]=arr[j+1];
            arr[j+1]=temp;
        }
    }
}
console.log(JSON.stringify(arr));//结果[10,20,30,40,60]

将 A 数组中某个属性的值,存储到 B 数组中

let arr1 = [{
    name: '自定义',
    age: 28
}, {
    name: '哈哈哈',
    age: 29
}]
const arr2 = arr1.map((item) => item.name);  //存储单个属性
const arr3 = arr1.map((item) => ({//存储多个属性
    myname: item.name,
    myage: item.age
}))

计算数组中所有元素项的总和

let aa=arr.reduce((prev,item)=>{
    return prev+item;
});
console.log(aa);

数组去重

let arr=[1,2,2,3,3,4,4,5,6,7,6,7,8]
for (let i=0;i<arr.length;i++){
    for (let j=i+1;j<arr.length;j++){
        if (arr[i]==arr[j]){
            arr.splice(j,1);
            j--;
        }
    }
}
console.log(arr);

return的作用

return 的作用是结束方法(终止函数)。

注意:

  • return 的值将会作为函数的执行结果返回,可以定义一个变量,来接收该结果。

  • 在函数中,return后的语句都不会执行(函数在执行完 return 语句之后停止并立即退出函数)

  • 如果return语句后不跟任何值,就相当于返回一个undefined

  • 如果函数中不写return,则也会返回undefined

  • 返回值可以是任意的数据类型,可以是对象,也可以是函数。

  • return 只能返回一个值。如果用逗号隔开多个值,则以最后一个为准。

fn() 和 fn 的区别【重要】

  • fn():调用函数。调用之后,还获取了函数的返回值。

  • fn:函数对象。相当于直接获取了整个函数对象。

break、continue、return 的区别

  • break :结束当前的循环体(如 for、while)

  • continue :跳出本次循环,继续执行下次循环(如 for、while)

  • return :1、退出循环。2、返回 return 语句中的值,同时结束当前的函数体内的代码,退出当前函数。

作用域链

  • 只要是代码,就至少有一个作用域

  • 写在函数内部的局部作用域

  • 如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域

作用域链:内部函数访问外部函数的变量,采用的是链式查找的方式来决定取哪个值,这种结构称之为作用域链。查找时,采用的是就近原则

函数内 this 的指向

根据函数的调用方式的不同,this 会指向不同的对象:

  • 1.以函数的形式(包括普通函数、定时器函数、立即执行函数)调用时,this 的指向永远都是 window。比如fun();相当于window.fun();

  • 2.以方法的形式调用时,this 指向调用方法的那个对象

  • 3.以构造函数的形式调用时,this 指向实例对象

  • 4.以事件绑定函数的形式调用时,this 指向绑定事件的对象

  • 5.使用 call 和 apply 调用时,this 指向指定的那个对象

求数组的最大最小值

const arr=[3,34,7,8,4];
const max=Math.max.apply(Math,arr); //最大值
const min=Math.min.apply(Math,arr); //最小值

高阶函数

函数 A 接收函数 B 作为参数,或者把函数 C 作为返回值输出时,我们称 函数 A 为高阶函数

高阶函数是 对其他函数进行操作 的函数

闭包函数

指有权访问另一个函数作用域中变量函数

如果这个作用域可以访问另外一个函数内部的局部变量,那就产生了闭包;而另外那个作用域所在的函数称之为闭包函数

function fn(){
    let a=20;
    function fn2(){
        console.log(a);
    };
    fn2()
}
fn();//此处fn为闭包函数

//另一种写法
function fn() {
    let a = 20;
    return function () {
        console.log(a);
    }
}
const foo = fn();
foo()

构造函数的概念

构造函数:是一种特殊的函数,主要用来创建和初始化对象,也就是为对象的成员变量赋初始值。它与 new 一起使用才有意义。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个构造函数里面。

类、实例

  • 使用同一个构造函数创建的对象,我们称为一类对象,也将一个构造函数称为一个

  • 通过一个构造函数创建的对象,称为该类的实例

instanceof

使用 instanceof 可以检查一个对象是否为一个类的实例

对象 instanceof 构造函数;

Json

JSON 的属性必须用双引号引起来

对象和 json 没有长度,json.length 的打印结果是 undefined。所以也就不能用 for 循环遍历

in 运算符

通过该运算符可以检查一个对象中是否含有指定的属性。如果有则返回 true,没有则返回 false。

'属性名' in 对象;

深拷贝和浅拷贝

拷贝引用的时候,是属于传址,而非传值

深拷贝会把对象里所有的数据重新复制到新的内存空间,是最彻底的拷贝。

const myObj = {
    name: 'qianguyihao',
    age: 28,
};

// 【写法1】浅拷贝:把 myObj 拷贝给 obj1
const obj1 = {};
Object.assign(obj1, myObj);

// 【写法2】浅拷贝:把 myObj 拷贝给 obj2
const obj2 = Object.assign({}, myObj);

// 【写法3】浅拷贝:把 myObj 拷贝给 obj31。注意,这里的 obj31 和 obj32 其实是等价的,他们指向了同一个内存地址
const obj31 = {};
const obj32 = Object.assign(obj31, myObj);

深拷贝

let obj1 = {
    name: 'qianguyihao',
    age: 28,
    info: {
        desc: 'hello',
    },
    color: ['red', 'blue', 'green'],
};
let obj2 = {};

deepCopy(obj2, obj1);
console.log(obj2);
obj1.info.desc = 'github';
console.log(obj2);

// 方法:深拷贝
function deepCopy(newObj, oldObj) {
    for (let key in oldObj) {
        // 获取属性值 oldObj[key]
        let item = oldObj[key];
        // 判断这个值是否是数组
        if (item instanceof Array) {
            newObj[key] = [];
            deepCopy(newObj[key], item);
        } else if (item instanceof Object) {
            // 判断这个值是否是对象
            newObj[key] = {};
            deepCopy(newObj[key], item);
        } else {
            // 简单数据类型,直接赋值
            newObj[key] = item;
        }
    }
}

事件的三要素:

谁引发的后续事件,谁就是事件源

  • 事件源:引发后续事件的html标签

  • 事件:js已经定义好了

  • 事件驱动程序:对样式和html的操作。也就是DOM

innerHTML和innerText的区别

  • value:标签的value属性。

  • innerHTML:双闭合标签里面的内容(包含标签)。

  • innerText:双闭合标签里面的内容(不包含标签)。(老版本的火狐用textContent)

无缝滚动轮播图

<!doctype html>
<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>无标题文档</title>
    <style type="text/css">
        * {
            padding: 0;
            margin: 0;
            list-style: none;
            border: 0;
        }

        .all {
            width: 500px;
            height: 200px;
            padding: 7px;
            border: 1px solid #ccc;
            margin: 100px auto;
            position: relative;
        }

        .screen {
            width: 500px;
            height: 200px;
            overflow: hidden;
            position: relative;
        }

        .screen li {
            width: 500px;
            height: 200px;
            overflow: hidden;
            float: left;
        }

        .screen ul {
            position: absolute;
            left: 0;
            top: 0px;
            width: 3000px;
        }

        .all ol {
            position: absolute;
            right: 10px;
            bottom: 10px;
            line-height: 20px;
            text-align: center;
        }

        .all ol li {
            float: left;
            width: 20px;
            height: 20px;
            background: #fff;
            border: 1px solid #ccc;
            margin-left: 10px;
            cursor: pointer;
        }

        .all ol li.current {
            background: yellow;
        }

        #arr {
            display: none;
        }

        #arr span {
            width: 40px;
            height: 40px;
            position: absolute;
            left: 5px;
            top: 50%;
            margin-top: -20px;
            background: #000;
            cursor: pointer;
            line-height: 40px;
            text-align: center;
            font-weight: bold;
            font-family: '黑体';
            font-size: 30px;
            color: #fff;
            opacity: 0.3;
            border: 1px solid #fff;
        }

        #arr #right {
            right: 5px;
            left: auto;
        }
    </style>

    <script>
        window.onload = function () {

            //需求:无缝滚动。
            //思路:赋值第一张图片放到ul的最后,然后当图片切换到第五张的时候
            //直接切换第六章,再次从第一张切换到第二张的时候先瞬间切换到
            //第一张图片,然后滑动到第二张
            //步骤:
            //1.获取事件源及相关元素。(老三步)
            //2.复制第一张图片所在的li,添加到ul的最后面。
            //3.给ol中添加li,ul中的个数-1个,并点亮第一个按钮。
            //4.鼠标放到ol的li上切换图片
            //5.添加定时器
            //6.左右切换图片(鼠标放上去隐藏,移开显示)


            //1.获取事件源及相关元素。(老三步)
            var all = document.getElementById("all");
            var screen = all.firstElementChild || all.firstChild;
            var imgWidth = screen.offsetWidth;
            var ul = screen.firstElementChild || screen.firstChild;
            var ol = screen.children[1];
            var div = screen.lastElementChild || screen.lastChild;
            var spanArr = div.children;

            //2.复制第一张图片所在的li,添加到ul的最后面。
            var ulNewLi = ul.children[0].cloneNode(true);
            ul.appendChild(ulNewLi);
            //3.给ol中添加li,ul中的个数-1个,并点亮第一个按钮。
            for (var i = 0; i < ul.children.length - 1; i++) {
                var olNewLi = document.createElement("li");
                olNewLi.innerHTML = i + 1;
                ol.appendChild(olNewLi)
            }
            var olLiArr = ol.children;
            olLiArr[0].className = "current";

            //4.鼠标放到ol的li上切换图片
            for (var i = 0; i < olLiArr.length; i++) {
                //自定义属性,把索引值绑定到元素的index属性上
                olLiArr[i].index = i;
                olLiArr[i].onmouseover = function () {
                    //排他思想
                    for (var j = 0; j < olLiArr.length; j++) {
                        olLiArr[j].className = "";
                    }
                    this.className = "current";
                    //鼠标放到小的方块上的时候索引值和key以及square同步
//                    key = this.index;
//                    square = this.index;
                    key = square = this.index;
                    //移动盒子
                    animate(ul, -this.index * imgWidth);
                }
            }

            //5.添加定时器
            var timer = setInterval(autoPlay, 1000);

            //固定向右切换图片
            //两个定时器(一个记录图片,一个记录小方块)
            var key = 0;
            var square = 0;

            function autoPlay() {
                //通过控制key的自增来模拟图片的索引值,然后移动ul
                key++;
                if (key > olLiArr.length) {
                    //图片已经滑动到最后一张,接下来,跳转到第一张,然后在滑动到第二张
                    ul.style.left = 0;
                    key = 1;
                }
                animate(ul, -key * imgWidth);
                //通过控制square的自增来模拟小方块的索引值,然后点亮盒子
                //排他思想做小方块
                square++;
                if (square > olLiArr.length - 1) {//索引值不能大于等于5,如果等于5,立刻变为0;
                    square = 0;
                }
                for (var i = 0; i < olLiArr.length; i++) {
                    olLiArr[i].className = "";
                }
                olLiArr[square].className = "current";
            }

            //鼠标放上去清除定时器,移开后在开启定时器
            all.onmouseover = function () {
                div.style.display = "block";
                clearInterval(timer);
            }
            all.onmouseout = function () {
                div.style.display = "none";
                timer = setInterval(autoPlay, 1000);
            }

            //6.左右切换图片(鼠标放上去显示,移开隐藏)
            spanArr[0].onclick = function () {
                //通过控制key的自增来模拟图片的索引值,然后移动ul
                key--;
                if (key < 0) {
                    //先移动到最后一张,然后key的值取之前一张的索引值,然后在向前移动
                    ul.style.left = -imgWidth * (olLiArr.length) + "px";
                    key = olLiArr.length - 1;
                }
                animate(ul, -key * imgWidth);
                //通过控制square的自增来模拟小方块的索引值,然后点亮盒子
                //排他思想做小方块
                square--;
                if (square < 0) {//索引值不能大于等于5,如果等于5,立刻变为0;
                    square = olLiArr.length - 1;
                }
                for (var i = 0; i < olLiArr.length; i++) {
                    olLiArr[i].className = "";
                }
                olLiArr[square].className = "current";
            }
            spanArr[1].onclick = function () {
                //右侧的和定时器一模一样
                autoPlay();
            }


            function animate(ele, target) {
                clearInterval(ele.timer);
                var speed = target > ele.offsetLeft ? 10 : -10;
                ele.timer = setInterval(function () {
                    var val = target - ele.offsetLeft;
                    ele.style.left = ele.offsetLeft + speed + "px";

                    if (Math.abs(val) < Math.abs(speed)) {
                        ele.style.left = target + "px";
                        clearInterval(ele.timer);
                    }
                }, 10)
            }
        }
    </script>
</head>

<body>
<div class="all" id='all'>
    <div class="screen" id="screen">
        <ul id="ul">
            <li><img src="images/1.jpg" width="500" height="200"/></li>
            <li><img src="images/2.jpg" width="500" height="200"/></li>
            <li><img src="images/3.jpg" width="500" height="200"/></li>
            <li><img src="images/4.jpg" width="500" height="200"/></li>
            <li><img src="images/5.jpg" width="500" height="200"/></li>
        </ul>
        <ol>

        </ol>
        <div id="arr">
            <span id="left"><</span>
            <span id="right">></span>
        </div>
    </div>
</div>
</body>
</html>

scrollTop 和 scrollLeft 封装方法

<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <style>
        body {
            height: 6000px;
            width: 5000px;
        }
    </style>
</head>
<body>
<script>
    //需求:封装一个兼容的scroll().返回的是对象,用scroll().top获取scrollTop,用scroll().left获取scrollLeft
    window.onscroll = function () {
//        var myScroll = scroll();
//        myScroll.top;
        console.log(scroll().top);
        console.log(scroll().left);
    }
    //函数封装
    function scroll() {
        return { //此函数的返回值是对象
            left: window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop,
            right: window.pageXOffset || document.body.scrollLeft || document.documentElement.scrollLeft
        }
    }
</script>
</body>
</html>

offset/scroll/client 的区别

区别1:宽高
  • offsetWidth = width + padding + border

  • offsetHeight = height + padding + border

  • scrollWidth = 内容宽度(不包含border)

  • scrollHeight = 内容高度(不包含border)

  • clientWidth = width + padding

  • clientHeight = height + padding

区别2:上左

offsetTop/offsetLeft:

  • 调用者:任意元素。(盒子为主)
  • 作用:距离父系盒子中带有定位的距离。

scrollTop/scrollLeft:

  • 调用者:document.body.scrollTop(window调用)(盒子也可以调用,但必须有滚动条)
  • 作用:浏览器无法显示的部分(被卷去的部分)。

clientY/clientX:

  • 调用者:event
  • 作用:鼠标距离浏览器可视区域的距离(左、上)

输出键盘被按下按键的编码

document.onkeydown = function(event) {
    event = event || window.event;
    console.log(event.keyCode);
};

jQuery的入口函数

jQuery占用了我们两个变量:$ 和 jQuery($===jQuery)

//1.文档加载完毕,图片不加载的时候,就可以执行这个函数。
$(document).ready(function () {
   alert(1);
})

//2.文档加载完毕,图片不加载的时候,就可以执行这个函数。
$(function () {
   alert(1);
});

//3.文档加载完毕,图片也加载完毕的时候,在执行这个函数。
$(window).ready(function () {
   alert(1);
})

let 和 const 的特点【重要】

  • 不属于顶层对象 Window
  • 不允许重复声明
  • 不存在变量提升
  • 暂时性死区
  • 支持块级作用域

相反, 用var声明的变量:存在变量提升、可以重复声明、没有块级作用域

const 来定义常量 (内存地址不能变化的量)

示例

// 注释方式和C很像,这是单行注释
/* 这是多行
   注释 */

// 语句可以以分号结束
doStuff();

// ... 但是分号也可以省略,每当遇到一个新行时,分号会自动插入(除了一些特殊情况)。
doStuff()

// 因为这些特殊情况会导致意外的结果,所以我们在这里保留分号。

///////////////////////////////////
// 1. 数字、字符串与操作符

// Javascript 只有一种数字类型(即 64位 IEEE 754 双精度浮点 double)。
// double 有 52 位表示尾数,足以精确存储大到 9✕10¹⁵ 的整数。
3; // = 3
1.5; // = 1.5

// 所有基本的算数运算都如你预期。
1 + 1; // = 2
0.1 + 0.2; // = 0.30000000000000004
8 - 1; // = 7
10 * 2; // = 20
35 / 5; // = 7

// 包括无法整除的除法。
5 / 2; // = 2.5

// 位运算也和其他语言一样;当你对浮点数进行位运算时,
// 浮点数会转换为*至多* 32 位的无符号整数。
1 << 2; // = 4

// 括号可以决定优先级。
(1 + 3) * 2; // = 8

// 有三种非数字的数字类型
Infinity; // 1/0 的结果
-Infinity; // -1/0 的结果
NaN; // 0/0 的结果

// 也有布尔值。
true;
false;

// 可以通过单引号或双引号来构造字符串。
'abc';
"Hello, world";

// 用!来取非
!true; // = false
!false; // = true

// 相等 ===
1 === 1; // = true
2 === 1; // = false

// 不等 !=
1 !== 1; // = false
2 !== 1; // = true

// 更多的比较操作符 
1 < 10; // = true
1 > 10; // = false
2 <= 2; // = true
2 >= 2; // = true

// 字符串用+连接
"Hello " + "world!"; // = "Hello world!"

// 字符串也可以用 < 、> 来比较
"a" < "b"; // = true

// 使用“==”比较时会进行类型转换...
"5" == 5; // = true
null == undefined; // = true

// ...除非你是用 ===
"5" === 5; // = false
null === undefined; // = false 

// ...但会导致奇怪的行为
13 + !0; // 14
"13" + !0; // '13true'

// 你可以用`charAt`来得到字符串中的字符
"This is a string".charAt(0);  // = 'T'

// ...或使用 `substring` 来获取更大的部分。
"Hello world".substring(0, 5); // = "Hello"

// `length` 是一个属性,所以不要使用 ().
"Hello".length; // = 5

// 还有两个特殊的值:`null`和`undefined`
null;      // 用来表示刻意设置的空值
undefined; // 用来表示还没有设置的值(尽管`undefined`自身实际是一个值)

// false, null, undefined, NaN, 0 和 "" 都是假的;其他的都视作逻辑真
// 注意 0 是逻辑假而  "0"是逻辑真,尽管 0 == "0"。

///////////////////////////////////
// 2. 变量、数组和对象

// 变量需要用`var`关键字声明。Javascript是动态类型语言,
// 所以你无需指定类型。 赋值需要用 `=` 
var someVar = 5;

// 如果你在声明时没有加var关键字,你也不会得到错误...
someOtherVar = 10;

// ...但是此时这个变量就会在全局作用域被创建,而非你定义的当前作用域

// 没有被赋值的变量都会被设置为undefined
var someThirdVar; // = undefined

// 对变量进行数学运算有一些简写法:
someVar += 5; // 等价于 someVar = someVar + 5; someVar 现在是 10 
someVar *= 10; // 现在 someVar 是 100

// 自增和自减也有简写
someVar++; // someVar 是 101
someVar--; // 回到 100

// 数组是任意类型组成的有序列表
var myArray = ["Hello", 45, true];

// 数组的元素可以用方括号下标来访问。
// 数组的索引从0开始。
myArray[1]; // = 45

// 数组是可变的,并拥有变量 length。
myArray.push("World");
myArray.length; // = 4

// 在指定下标添加/修改
myArray[3] = "Hello";

// javascript中的对象相当于其他语言中的“字典”或“映射”:是键-值对的无序集合。
var myObj = {key1: "Hello", key2: "World"};

// 键是字符串,但如果键本身是合法的js标识符,则引号并非是必须的。
// 值可以是任意类型。
var myObj = {myKey: "myValue", "my other key": 4};

// 对象属性的访问可以通过下标
myObj["my other key"]; // = 4

// ... 或者也可以用 . ,如果属性是合法的标识符
myObj.myKey; // = "myValue"

// 对象是可变的;值也可以被更改或增加新的键
myObj.myThirdKey = true;

// 如果你想要获取一个还没有被定义的值,那么会返回undefined
myObj.myFourthKey; // = undefined

///////////////////////////////////
// 3. 逻辑与控制结构

// 本节介绍的语法与Java的语法几乎完全相同

// `if`语句和其他语言中一样。
var count = 1;
if (count == 3){
    // count 是 3 时执行
} else if (count == 4){
    // count 是 4 时执行
} else {
    // 其他情况下执行 
}

// while循环
while (true) {
    // 无限循环
}

// Do-while 和 While 循环很像 ,但前者会至少执行一次
var input;
do {
    input = getInput();
} while (!isValid(input))

// `for`循环和C、Java中的一样:
// 初始化; 继续执行的条件; 迭代。
for (var i = 0; i < 5; i++){
    // 遍历5次
}

// && 是逻辑与, || 是逻辑或
if (house.size == "big" && house.colour == "blue"){
    house.contains = "bear";
}
if (colour == "red" || colour == "blue"){
    // colour是red或者blue时执行
}

// && 和 || 是“短路”语句,它在设定初始化值时特别有用 
var name = otherName || "default";

// `switch`语句使用`===`检查相等性。
// 在每一个case结束时使用 'break'
// 否则其后的case语句也将被执行。 
grade = 'B';
switch (grade) {
  case 'A':
    console.log("Great job");
    break;
  case 'B':
    console.log("OK job");
    break;
  case 'C':
    console.log("You can do better");
    break;
  default:
    console.log("Oy vey");
    break;
}

///////////////////////////////////
// 4. 函数、作用域、闭包

// JavaScript 函数由`function`关键字定义
function myFunction(thing){
    return thing.toUpperCase();
}
myFunction("foo"); // = "FOO"

// 注意被返回的值必须开始于`return`关键字的那一行,
// 否则由于自动的分号补齐,你将返回`undefined`。
// 在使用Allman风格的时候要注意.
function myFunction()
{
    return // <- 分号自动插在这里
    {
        thisIsAn: 'object literal'
    }
}
myFunction(); // = undefined

// javascript中函数是一等对象,所以函数也能够赋给一个变量,
// 并且被作为参数传递 —— 比如一个事件处理函数:
function myFunction(){
    // 这段代码将在5秒钟后被调用
}
setTimeout(myFunction, 5000);
// 注意:setTimeout不是js语言的一部分,而是由浏览器和Node.js提供的。

// 函数对象甚至不需要声明名称 —— 你可以直接把一个函数定义写到另一个函数的参数中
setTimeout(function(){
    // 这段代码将在5秒钟后被调用
}, 5000);

// JavaScript 有函数作用域;函数有其自己的作用域而其他的代码块则没有。
if (true){
    var i = 5;
}
i; // = 5 - 并非我们在其他语言中所期望得到的undefined

// 这就导致了人们经常使用的“立即执行匿名函数”的模式,
// 这样可以避免一些临时变量扩散到全局作用域去。
(function(){
    var temporary = 5;
    // 我们可以访问修改全局对象("global object")来访问全局作用域,
    // 在web浏览器中是`window`这个对象。 
    // 在其他环境如Node.js中这个对象的名字可能会不同。
    window.permanent = 10;
})();
temporary; // 抛出引用异常ReferenceError
permanent; // = 10

// javascript最强大的功能之一就是闭包。
// 如果一个函数在另一个函数中定义,那么这个内部函数就拥有外部函数的所有变量的访问权,
// 即使在外部函数结束之后。
function sayHelloInFiveSeconds(name){
    var prompt = "Hello, " + name + "!";
    // 内部函数默认是放在局部作用域的,
    // 就像是用`var`声明的。
    function inner(){
        alert(prompt);
    }
    setTimeout(inner, 5000);
    // setTimeout是异步的,所以 sayHelloInFiveSeconds 函数会立即退出,
    // 而 setTimeout 会在后面调用inner
    // 然而,由于inner是由sayHelloInFiveSeconds“闭合包含”的,
    // 所以inner在其最终被调用时仍然能够访问`prompt`变量。
}
sayHelloInFiveSeconds("Adam"); // 会在5秒后弹出 "Hello, Adam!"


///////////////////////////////////
// 5. 对象、构造函数与原型

//  对象可以包含方法。
var myObj = {
    myFunc: function(){
        return "Hello world!";
    }
};
myObj.myFunc(); // = "Hello world!"

// 当对象中的函数被调用时,这个函数可以通过`this`关键字访问其依附的这个对象。
myObj = {
    myString: "Hello world!",
    myFunc: function(){
        return this.myString;
    }
};
myObj.myFunc(); // = "Hello world!"

// 但这个函数访问的其实是其运行时环境,而非定义时环境,即取决于函数是如何调用的。
// 所以如果函数被调用时不在这个对象的上下文中,就不会运行成功了。
var myFunc = myObj.myFunc;
myFunc(); // = undefined

// 相应的,一个函数也可以被指定为一个对象的方法,并且可以通过`this`访问
// 这个对象的成员,即使在函数被定义时并没有依附在对象上。
var myOtherFunc = function(){
    return this.myString.toUpperCase();
}
myObj.myOtherFunc = myOtherFunc;
myObj.myOtherFunc(); // = "HELLO WORLD!"

// 当我们通过`call`或者`apply`调用函数的时候,也可以为其指定一个执行上下文。
var anotherFunc = function(s){
    return this.myString + s;
}
anotherFunc.call(myObj, " And Hello Moon!"); // = "Hello World! And Hello Moon!"

// `apply`函数几乎完全一样,只是要求一个array来传递参数列表。
anotherFunc.apply(myObj, [" And Hello Sun!"]); // = "Hello World! And Hello Sun!"

// 当一个函数接受一系列参数,而你想传入一个array时特别有用。
Math.min(42, 6, 27); // = 6
Math.min([42, 6, 27]); // = NaN (uh-oh!)
Math.min.apply(Math, [42, 6, 27]); // = 6

// 但是`call`和`apply`只是临时的。如果我们希望函数附着在对象上,可以使用`bind`。
var boundFunc = anotherFunc.bind(myObj);
boundFunc(" And Hello Saturn!"); // = "Hello World! And Hello Saturn!"

// `bind` 也可以用来部分应用一个函数(柯里化)。
var product = function(a, b){ return a * b; }
var doubler = product.bind(this, 2);
doubler(8); // = 16

// 当你通过`new`关键字调用一个函数时,就会创建一个对象,
// 而且可以通过this关键字访问该函数。
// 设计为这样调用的函数就叫做构造函数。
var MyConstructor = function(){
    this.myNumber = 5;
}
myNewObj = new MyConstructor(); // = {myNumber: 5}
myNewObj.myNumber; // = 5

// 每一个js对象都有一个‘原型’。当你要访问一个实际对象中没有定义的一个属性时,
// 解释器就回去找这个对象的原型。

// 一些JS实现会让你通过`__proto__`属性访问一个对象的原型。
// 这虽然对理解原型很有用,但是它并不是标准的一部分;
// 我们后面会介绍使用原型的标准方式。
var myObj = {
    myString: "Hello world!"
};
var myPrototype = {
    meaningOfLife: 42,
    myFunc: function(){
        return this.myString.toLowerCase()
    }
};

myObj.__proto__ = myPrototype;
myObj.meaningOfLife; // = 42

// 函数也可以工作。
myObj.myFunc() // = "hello world!"

// 当然,如果你要访问的成员在原型当中也没有定义的话,解释器就会去找原型的原型,以此类推。
myPrototype.__proto__ = {
    myBoolean: true
};
myObj.myBoolean; // = true

// 这其中并没有对象的拷贝;每个对象实际上是持有原型对象的引用。
// 这意味着当我们改变对象的原型时,会影响到其他以这个原型为原型的对象。
myPrototype.meaningOfLife = 43;
myObj.meaningOfLife; // = 43

// 我们知道 `__proto__` 并非标准规定,实际上也没有标准办法来修改一个已存在对象的原型。
// 然而,我们有两种方式为指定原型创建一个新的对象。

// 第一种方式是 Object.create,这个方法是在最近才被添加到Js中的,
// 因此并不是所有的JS实现都有这个方法
var myObj = Object.create(myPrototype);
myObj.meaningOfLife; // = 43

// 第二种方式可以在任意版本中使用,不过必须通过构造函数。
// 构造函数有一个属性prototype。但是它 *不是* 构造函数本身的原型;相反,
// 是通过构造函数和new关键字创建的新对象的原型。
MyConstructor.prototype = {
    myNumber: 5,
    getMyNumber: function(){
        return this.myNumber;
    }
};
var myNewObj2 = new MyConstructor();
myNewObj2.getMyNumber(); // = 5
myNewObj2.myNumber = 6
myNewObj2.getMyNumber(); // = 6

// 字符串和数字等内置类型也有通过构造函数来创建的包装类型
var myNumber = 12;
var myNumberObj = new Number(12);
myNumber == myNumberObj; // = true

// 但是它们并非严格等价
typeof myNumber; // = 'number'
typeof myNumberObj; // = 'object'
myNumber === myNumberObj; // = false
if (0){
    // 这段代码不会执行,因为0代表假
}

// 不过,包装类型和内置类型共享一个原型,
// 所以你实际可以给内置类型也增加一些功能,例如对string:
String.prototype.firstCharacter = function(){
    return this.charAt(0);
}
"abc".firstCharacter(); // = "a"

// 这个技巧经常用在“代码填充”中,来为老版本的javascript子集增加新版本js的特性,
// 这样就可以在老的浏览器中使用新功能了。

// 比如,我们知道Object.create并没有在所有的版本中都实现,
// 但是我们仍然可以通过“代码填充”来实现兼容:
if (Object.create === undefined){ // 如果存在则不覆盖
    Object.create = function(proto){
        // 用正确的原型来创建一个临时构造函数
        var Constructor = function(){};
        Constructor.prototype = proto;
        // 之后用它来创建一个新的对象
        return new Constructor();
    }
}

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