Git Product home page Git Product logo

blog's Introduction

blog's People

Contributors

chenbj2333 avatar

Watchers

 avatar  avatar

blog's Issues

做一个自己的npm包

1 首先,我使用angular-cli自动创建一个angular4的项目。
2 写package.json文件

{
  "name": "ng2-package-cbjtest",
  // 这个是发布出去的npm包的名字,如果npm上已经有这个名字的包了,npm publish的时候会报错误403。顺便说一句,如果没登陆npm会报错误400。
  "version": "0.0.2",
  // 这个是版本号,每次更新代码或者readme之后,需要修改版本号再发布。
  "license": "MIT",
  "scripts": {
    "ng": "ng",
    "start": "ng serve --port 4202",
    "build": "npm run css && ./node_modules/.bin/ngc -p tsconfig.build.json && rollup -c",
    "build:cli": "ng build --aot",
    "build:lib": "nglb --rootDir src/ng2-package --outDir lib-dist",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e",
    "clean": "rimraf node_modules dist dist-lib",
    "clean:install": "npm run clean && npm install",
    "prepublish": "npm run build:lib"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/chenbingjie2333/ng2-package.git"
  },
  "keywords": [
    "ng2",
    "demo"
  ],
  "author": "cbj",
  "private": false,
  "bugs": {
    "url": "https://github.com/chenbingjie2333/ng2-package/issues"
  },
  "homepage": "https://github.com/chenbingjie2333/ng2-package#readme",
  "main": "./lib-dist/ng2-package.module.js",
  "module": "./lib-dist/ng2-package.module.js",
  "types": "./lib-dist/ng2-package.module.d.ts",
  "peerDependencies": {
    "@angular/common": "^4.0.0",
    "@angular/core": "^4.0.0",
    "@angular/forms": "^4.0.0",
    "rxjs": "^5.1.0",
    "zone.js": "^0.8.4"
  },
  "dependencies": {
    "@angular/animations": "^4.0.0",
    "@angular/common": "^4.0.0",
    "@angular/compiler": "^4.0.0",
    "@angular/core": "^4.0.0",
    "@angular/forms": "^4.0.0",
    "@angular/http": "^4.0.0",
    "@angular/platform-browser": "^4.0.0",
    "@angular/platform-browser-dynamic": "^4.0.0",
    "@angular/router": "^4.0.0",
    "core-js": "^2.4.1",
    "rxjs": "^5.1.0",
    "zone.js": "^0.8.4"
  },
  "devDependencies": {
    "angular-library-builder": "^1.3.1",
    "rimraf": "^2.6.1",
    "@angular/cli": "1.2.1",
    "@angular/compiler-cli": "^4.0.0",
    "@angular/language-service": "^4.0.0",
    "@types/jasmine": "~2.5.53",
    "@types/jasminewd2": "~2.0.2",
    "@types/node": "~6.0.60",
    "codelyzer": "~3.0.1",
    "jasmine-core": "~2.6.2",
    "jasmine-spec-reporter": "~4.1.0",
    "karma": "~1.7.0",
    "karma-chrome-launcher": "~2.1.1",
    "karma-cli": "~1.0.1",
    "karma-coverage-istanbul-reporter": "^1.2.1",
    "karma-jasmine": "~1.1.0",
    "karma-jasmine-html-reporter": "^0.2.2",
    "protractor": "~5.1.2",
    "ts-node": "~3.0.4",
    "tslint": "~5.3.2",
    "typescript": "~2.3.3"
  }
}

3 写包的module

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { AppPackageRootComponent } from './ng2-package.component';
import { PackagePage1Component } from './component/page1Component/page1.component';
import { PackagePage2Component } from './component/page2Component/page2.component';

const appRoutes: Routes = [
  { path: '', redirectTo: '/page1',pathMatch: 'full' },
  { path: 'page1', component: PackagePage1Component},
  { path: 'page2', component: PackagePage2Component}
];
@NgModule({
  declarations: [
    AppPackageRootComponent,
    PackagePage1Component,
    PackagePage2Component
  ],
  imports: [
    BrowserModule,
    RouterModule.forRoot(appRoutes)
  ],
  providers: [],
  exports: [
    AppPackageRootComponent
  ]
})
export class MainModule { }
// 这个MainModule 就是别人使用你的包时要导入的模块啦

4 具体的结构
repository
5 发布
先登录,再发布

npm login 
npm publish

6 更新
(1)修改代码
(2)修改package.json中的版本号
(3)npm publish
7 使用
(1)安装

npm install ng2-package-cbjtest

(2)引用

import { MainModule } from 'ng2-package-cbjtest';
@NgModule({
 ...
  imports: [
    ...
    MainModule,
    ...
  ],
  ...
})

使用可观察对象的数据架构

十分努力的啃一遍官网(缓慢学习中)

RxJS API 中文版
构建流式应用—RxJS详解
RxJS 中文文档
RxMarbles

rxjs是一个使用observable sequences(可观察序列)来构建异步和基于事件的程序的库。它提供了一个核心类型Observable,以及围绕着核心类型的类型Observer,Schedulers , Subjects,还有一些衍生自数组的operators,像map,filter,reduce,every等等。这些让我们可以以集合的方式处理异步事件。
简单的说,rxjs:reactive extensions for javascript,就是把随时间不断变化的数据,状态,事件等转化成可被观察的序列。

rxjs中解决异步事件管理的一些基本概念:

  • Observable(可观察对象):代表一个可调用的未来的值或者事件的集合。
  • Observer(观察者):回调集合,监听Observab发送的值。
  • Subscription:代表Observable的执行,主要用来取消执行。
  • Operators:纯函数,处理集合。
  • Subject:EventEmitter,将一个值或者事件多播到多个观察者的唯一途径。
  • Schedulers:集中调度控制并发。

first examples

通常注册事件监听的方式:

let button = document.querySelector('button');
button.addEventListener('click', () => console.log('clicked!'));

使用rxjs创建方式:

let button = document.querySelector('button');
Observable.fromEvent(button, 'click').subscribe(
  () => console.log('clicked!')
);

Purity 纯函数

什么叫纯函数?
给出同样的参数,该函数总是得出同样结果。不会调用任何会对外界产生影响的函数。比如没有数据库调用,没有http请求,也不会改变外部的数据结构。
使用rxjs来隔离状态

let button = document.querySelector('button');
Observable.fromEvent(button, 'click')
.scan(count => count + 1, 0)
.subscribe(count => console.log(`Clicked ${count} times`));

scan操作符把每个中间过程中计算出的结果值发送出去,scan(count => count + 1, 0)中,0是初始值,每次点击时,会返回count => count + 1的结果,并把这个结果作为下一次函数运行时的初始值。

Flow 流

关于流的一些概念

  1. 在应用中,流扮演着和承诺(promise)一样的角色。promise发出单个值,而流发出多个值。可以在流上持续响应数据的变化。
  2. 在响应式编程中,代码订阅了数据变化时接收通知,流会把数据推送给这些订阅者。
  3. RxJS是函数式的。
  4. 流是可以组合的。可以把流想像成一个贯穿数据的操作管道。可以订阅流中的任何部分,也可以组合他们创建新的流。
    rxjs有一系列的operators,他们能帮助你去控制事件如何流入可观察对象observables。
    例如
let button = document.querySelector('button');
Observable.fromEvent(button, 'click').throttleTime(1000)
.scan(count => count + 1, 0)
.subscribe(count => console.log(`Clicked ${count} times`));

Values 值

可以通过可观察对象来转化值
例如,下面的程序是在每次点击鼠标时获取x坐标的位置

let button = document.querySelector('button');
Observable.fromEvent(button, 'click')
.throttleTime(1000)
.map(event => event.clientX)
.scan((count, clientX) => count + clientX, 0)
.subscribe(count => console.log(count));

Observable 可观察对象

pull and push

data Producer 和data Consumer之间的交流是通过pull(拉)和push(推)这两种不同的方式。
什么是pull?
data Consumer决定何时从data Producer那里获取数据,但是data Producer并不会意识到什么时候数据将会被发送给data Consumer。
1498634084 1
什么是push?
data Producer决定何时发送数据给data Consumer,data Consumer不会在接收数据之前意识到它将要接收这个数据。
promise是现在js中最常见的push体系。一个promise发送一个resolved value来注册一个回调,不同于函数的地方是,promise决定何时数据才被推送到这个回调函数中。
rxjs引入了observables,一个observable对象是一个i额产生多值的生产者,并推送给observer。

Observables as generalizations of functions

可观察对象像一个零参的函数,但是允许返回多个值。
订阅一个可观察对象类似于调用一个函数。

Anatomy of an Observable 分析可观察对象

  • Creating Observables
  • Subscribing to Observables
  • Executing the Observable
  • Disposing Observable
    创建(create)一个可观察对象,观察者订阅(subscribe)它,执行(execute)它然后给观察者发送next/error/complete通知。他们的执行可能会被处理(disposed)。

Observer 观察者

什么是观察者?
观察者是可观察对象所发送数据的消费者。观察者简单的说是一组回调函数,分别对应一种被可观察对象发送的通知的类型:next,error和complete。
使用观察者需要订阅可观察对象。

Subscription 订阅

什么是订阅?
订阅通常是一个可观察对象的执行。订阅对象有一个重要的方法:unsubscribe,该方法不需要参数,仅仅用来废弃可观察对象所持有的资源。
订阅对象还有两个方法。一个是add,用于把一个订阅加进另一个订阅里。还有一个是remove,用于解除被add添加的子订阅。

let subscription = Observable.subscribe(x => console.log('first:' + x));
let childSubscription = Observable.subscribe(x => console.log('second:' + x));
subscription.add(childSubscription);

以下转自雨异博客

实例方法Operators

1 创建

  • 发射完数据更新自动关闭: from,fromPromise, of,range
  • 不发射直接关闭:empty
  • 抛出异常后关闭:throw
  • 不发射数据也不关闭:never
  • 保持发射数据且不自动关闭:timer,interval,fromEvent
  • 需要手动发射数据且不自动关闭: create,(包括subject的create)

2 转换

  • 1 : 1效果:map , mapTo , flatMap , scan , expand , pluck
  • 1 : N效果:concat , concatAll , concatMap , concatMapTo , merge, mergeAll, mergeMap, mergeMapTo, switchMap, switchMapTo
  • N : 1效果:buffer, bufferCount, bufferTime, bufferWhen
  • 1 : source效果:groupBy, window, windowCount, windowTime, windowWhen
  • 1 : sources效果:partition

3 过滤

  • 防抖动(一段时间内只取最新数据作为一次发射数据,其他数据取消发射):debounce, debounceTime, throttle(和debounce唯一区别是debounce取一段时间内最新的,而throttle忽略这段时间后,发现新值才发送), throttleTime
  • 去重(重叠的发射数据只去第一数据作为发射数据,其他相同数据取消发射):distinct, distinctUntilChanged
  • 定位(根据条件值去一个或部分几个数据作为对应发射数据,其他取消发射):elementAt, first, last, filter, take, takeLatst, takeUntil, takeWhile
  • 跳过(根据条件去除符合条件的,取剩下的值作为每次发射数据):skip, skipUntil, skipWhile, ignoreElements(忽略所有的,等同于empty)
  • 样本:sample, source=source1.sample(source2), 以source2发射数据时来发现最新一次source1发射的数据,作为source的发射数据

4 组合
做个source组合成新的souce

  • concat, concatAll和merge, mergeAll,在转换分类讲过了
  • combineLastest,source = source1.combineLastest(source2, func),source1和source2一旦发射数据,func会触发,拿到source1和source2最新的发射数据,返回新的数据,作为source的发射数据。
  • combineAll,同combineLastest,,source = sources.combineAll()
  • forkJoin,source = Rx.Observable.forkJoin(sources), 所有的sources都关闭后,获取各自最新的发射数组组合为数组,作为source的发射数据
  • zip和forkJoin的区别是,zip是sources都有发送数据时,组合为一个数组作为source的发送数据,而sources任一source关闭了,则取source最后发射的数值。
  • zipAll,同concat对concatAll
  • startWith,source = source1.startWith(value), 表示在source1的最前面注入第一次发射数据
  • withLastestFrom, soruce = source1.withLastestFrom(source2, func), 表示source1每次发射数据时,获取source2最新发射的数据,如果存在则func处理得到新的数组作为source的发射数据

5 判断

  • find和findIndex分别是指定发射数据和发射数据的下标(第几次发送的),应该放到过滤分类才合理
  • isEmpty, every, include等,判断是否为真,判断的结果当做是source的发射数据

6 错误处理

  • catch,source在Operators调用过程中出现的异常,都可以在catch捕获到,同时可以返回新的source,因为出现异常的当前source会自动销毁掉。
  • retry,source = source.retry(times), source的所有发射,重复来几遍。
  • retryWhen,根据条件来决定来几遍,只有当条件为false时才跳出循环。

7 工具

  • do,在每次响应订阅前,可以通过source.do(func),做一些提前处理等任何动作,比如打印一下发射的数据等。
  • delay, delayWhen,每次发送数据时,都延迟一定时间间隔后再发送。
  • observeOn, 设置scheduler,即发射数据的响应方式,Schedulers详细查看地址, 这里不讲解了,项目中应用得不多。
  • subcribeOn, timeInterval设置sheduler
  • toPromise, source转成promise,可以通过promise.then达到source.subscribe的效果
  • toArray,把source所有发射的数据,组成数组输出。

8 计算
把source的所有发射数据进行指定计算后,得出的数据作为新source的发射数据,计算方法分别有:max, min, count, reduce, average等

9 其他

  • cache, source = source1.cache(1);共享source1的订阅结果,即不管source订阅几回,响应方法接收到的发射数据都是同一份。
  • 共享source订阅结果很重要,因为组合等方法组合多个source时,其中包含sourceA,同时sourceA还需要单独订阅其结果,在不用cache情况下,sourceA会产生2个subscription,即2个订阅实例,但是我们更希望是能达到sourceA发生变化时,都能通知到所有的组合sourceA的source。
  • publish,publishSource = source.publish(),让source的订阅的工作延后,即source不会发射数据,而是等到publishSource.connect()调用后才开发发射数据。效果和delay很相似,不同的是可以控制合适发射。
  • share,当source订阅多次,那么每次响应时do都会调用多次,通过share合并响应,则source发射一次数据更新,多次响应当当一次响应处理,do也调用一次。

e2e测试

angular中测试程序有两种主要方法: 端对端测试 和 单元测试。

端对端测试

使用一种自上而下的方法进行测试,写测试时将程序视为一个黑盒,与程序交互如同真实用户一样,从旁观者的角度评判程序是否达标。
在angular中,最常用的工具叫做protractor。

  1. 安装
    npm install protractor --save-dev
    npm install lite-server --save-dev
    npm install canonical-path --save-dev
    npm install jasmine-core --save-dev
    npm install @types/jasmine --save-dev
    npm install concurrently --save
    安装protractor-jasmine2-html-reporter用于显示报告和截图
    npm install protractor-jasmine2-html-reporter --save-dev
  2. 添加bs-config.e2e.json文件
{
  "open": false,
  "logLevel": "silent",
  "port": 8080,
  "server": {
    "baseDir": "src",
    "routes": {
      "/node_modules": "node_modules"
    },
    "middleware": {
      "0": null
    }
  }
}
  1. 添加protractor.config.js文件
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts

/*global jasmine */
var SpecReporter = require('jasmine-spec-reporter').SpecReporter;
var Jasmine2HtmlReporter = require('protractor-jasmine2-html-reporter');

exports.config = {
    allScriptsTimeout: 11000,
    specs: [
        './e2e/**/*.e2e-spec.ts'
    ],
    capabilities: {
        'browserName': 'chrome',
        'chromeOptions': {
            'args': ['--headless', '--disable-gpu'],
            'mobileEmulation': {
                'deviceName': 'iPhone 5'
                // "deviceMetrics": {
                //     "width": 360,
                //     "height": 640,
                //     "zoom": 0.7
                // }
            }
        }
    },
    directConnect: true,
    baseUrl: 'http://localhost:8100/',
    framework: 'jasmine2',
    jasmineNodeOpts: {
        showColors: true,
        defaultTimeoutInterval: 30000,
        print: function() {}
    },
    useAllAngular2AppRoots: true,
    beforeLaunch: function() {
        require('ts-node').register({
            project: 'e2e'
        });
    },
    onPrepare: function() {
        jasmine.getEnv().addReporter(new SpecReporter());
        jasmine.getEnv().addReporter(
        new Jasmine2HtmlReporter({
          savePath: './e2e_report/',
          screenshotsFolder: 'images',
          takeScreenshots: true
        })
      );
    }
};
  1. packeage.json
    "build:e2e": "tsc -p e2e/",
    "serve:e2e": "lite-server -c=bs-config.e2e.json",
    "pree2e": "npm run build:e2e",
    "e2e": "concurrently \"npm run serve:e2e\" \"npm run protractor\" --kill-others --success first",
    "preprotractor": "webdriver-manager update",  //第一次使用添加,之后由于“墙”的问题可以去掉。
    "protractor": "protractor protractor.config.js"

报错Could not find update-config.json
原因应该是本机全局装了webdriver,所以在protractor.config.json中去掉directConnect: true

如果网比较好的,可以用以下方式:

npm install protractor --save-dev
npm install -g webdriver-manager

packeage.json

"e2e": "webdriver-manager update --standalone false --gecko false; protractor ./protractor.config.js"

基于typescript的redux(ng-book2)

为什么要使用redux

1 属性的间接传递:为了让任何组件都可以获取到应用的状态,我们会使用inputs传递值,这可能导致我们会借助很多中间组件来传递状态,但是这些中间组件不使用这些值也不关心这些值的状态。
2 重构不灵活:传递inputs会贯穿整个组件树,从而导致父子组件之间的耦合。
3 状态树和dom树不匹配。
4 应用中到处都是状态:如果通过组件来管理状态,就很难获取应用整体状态的快照。很难知道哪个组件拥有一条特定的数据以及哪些组件关心该数据的变化。
redux就是把所有应用状态都存在同一个地方。

redux的核心概念

1 应用的所有数据都放在一个叫做state的数据结构之中,state放在store中。
2 应用从store中读取state。
3 store永远不会被直接修改。
4 action描述发生了什么,由用户交互触发。
5 通过调用一个reducer的函数来结合旧的state和action会创建出新的state。

reducer是什么

接收旧的state和action返回新的state。reducer是一个纯函数,它不能直接修改当前的state,也不会使用除参数之外的任何数据。

自己目前常用的git命令

(搭配使用github desktop)

  1. 把github上项目拉到本地
  • 在github上新建一个项目。用github desktop clone自己建好(或者自己fork)的仓库,如果是fork的话再添加远程仓库
  • 添加远程仓库的命令
    git remote add propersoft-cn https://github.com/propersoft-cn/ihos.git
  • 如果自己的远程仓库里有了,想在本地添加一个新分支例如app-dev,可以使用一下命令
git remote show origin            更新连接信息
git remote update                   更新连接信息
git checkout -b app-dev propersoft-cn/app-dev
git push origin app-dev
  1. 操作常用命令
  • 查看分支 git branch
  • 基于某个分支创建分支:在某个分支下使用命令 git branch XXX
  • 切换分支 git checkout XXX
  • 删除本地分支 git branch -D XXX
  • 删除远程分支
命令:$ Git push origin 【空格】【冒号】【你的分支名字】
比如我github上有master和feature分支,我现在想着删除feature分支,命令如下:
$ git push origin :feature
  • 查看连接信息 git remote -v
  • 查看提交记录 git log
  • 追溯版本。哈希值前6位。 git reset --hard 8eae84(commit 8eae8436ec34553......)
  1. 习惯
    我习惯保持一个分支与远程分支同步。比如远程仓库有一个分支app,在本地仓库里也建一个分支app。每次要修改代码前,首先同步一下app分支 git pull propersoft-cn app ,然后基于app分支创建一个新的分支。改完代码后 git push origin app-new。如果有冲突,在app-new分支下 git pull propersoft-cn app(有时候需要命令行中ctrl+z退出),然后可以在github desktop中看到有冲突的文件(编辑器里也能看到),修改再提交即可。

  2. 自己的仓库

  • 更新主分支master
  • 基于master建新分支master-demo,写完代码后push到origin
  • 切换到master分支,改动同步到master中 => git merge master-demo
  • 提交 => git push

webpack中的source map

webpack中的source map

出现的原因

  • 生产环境中的js代码基本都进行了压缩合并或者是其他语言编译成的js,这些让实际运行的代码不同于开发代码,很难debug。所以source map就是想解决这个问题。

什么是source map

  • source map是一个信息文件,记录着转换后代码的位置所对应的转换前代码的位置。

webpack中的source map(devtool分类中)

+++ super fast, ++ fast, + pretty fast, o medium, - pretty slow, -- slow

devtool build rebuild production quality
(none) +++ +++ yes bundled code
eval +++ +++ no generated code
cheap-eval-source-map + ++ no transformed code (lines only)
cheap-module-eval-source-map o ++ no original source (lines only)
eval-source-map -- + no original source
cheap-source-map + o yes transformed code (lines only)
cheap-module-source-map o - yes original source (lines only)
inline-cheap-source-map + o no transformed code (lines only)
inline-cheap-module-source-map o - no original source (lines only)
source-map -- -- yes original source
inline-source-map -- -- no original source
hidden-source-map -- -- yes original source
nosources-source-map -- -- yes without source content

其中有5个关键字:evalsource-mapcheapmoduleinline

  • eval: eval模式是使用eval将webpack中每个模块包裹,然后在模块末尾添加模块来源//# souceURL, 依靠souceURL找到原始代码的位置。包含eval关键字的配置项并不单独产生.map文件
  • source-map: 产生.map文件
  • cheap: 如果包含cheap关键字,则产生的.map文件不包含列信息,也不包含loader的sourcemap。也就是说当你在浏览器中点击该代码的位置时, 光标只定位到行数,不定位到具体字符位置。而不包含cheap关键字时, 点击控制台log将会定位到字符位置。
  • module: 包含loader的sourcemap(比如jsx to js ,babel的sourcemap)
  • inline: 将.map作为DataURI嵌入,不单独生成.map文件(这个配置项比较少见)

这么多模式用哪个好?

  • 开发环境推荐:
cheap-module-eval-source-map
  • 生产环境推荐:
cheap-module-source-map

这也是下版本 webpack 使用 -d 命令启动 debug 模式时的默认选项

原因如下:

  1. 使用 cheap 模式可以大幅提高 souremap 生成的效率。大部分情况我们调试并不关心列信息,而且就算 sourcemap 没有列,有些浏览器引擎(例如 v8) 也会给出列信息。
  2. 使用 eval 方式可大幅提高持续构建效率。官方文档提供的速度对比表格可以看到 eval 模式的编译速度很快。
  3. 使用 module 可支持 babel 这种预编译工具(在 webpack 里做为 loader 使用)。
  4. 使用 eval-source-map 模式可以减少网络请求。这种模式开启 DataUrl 本身包含完整 sourcemap 信息,并不需要像 sourceURL 那样,浏览器需要发送一个完整请求去获取 sourcemap 文件,这会略微提高点效率。而生产环境中则不宜用 eval,这样会让文件变得极大。
在customize-cra中可以如下写
const rewiredMap = () => config => {
    config.devtool = config.mode === 'production' ? 'cheap-module-source-map' : 'cheap-module-eval-source-map';
    return config;
};

module.exports = override(
    // 关闭mapSource
    rewiredMap()
);
参考文章

打破砂锅问到底:详解Webpack中的sourcemap

webpack——devtool里的7种SourceMap模式

重学前端笔记-HTML语义

语义类标签是什么,使用它有什么好处?

语义类标签也是大家工作中经常会用到的一类标签,它们的特点是视觉表现上互相都差不多,主要的区别在于它们表示了不同的语义,比如大家会经常见到的 section、nav、p,这些都是语义类的标签。

  • 语义类标签对开发者更为友好,使用语义类标签增强了可读性,即便是在没有 CSS 的时候,开发者也能够清晰地看出网页的结构,也更为便于团队的开发和维 护。
  • 除了对人类友好之外,语义类标签也十分适宜机器阅读。它的文字表现力丰富,更适合搜索引擎检索(SEO),也可以让搜索引擎爬虫更好地获取到更多有效信 息,有效提升网页的搜索量,并且语义类还可以支持读屏软件,根据文章可以自动生成目录等等。

举个例子:

html语义化1

  • aside: 表示跟文章主体不那么相关的部分,它可能包含导航、广告等工具性质的内容。aside 很容易被理解为侧边栏,实际上二者是包含关系,侧边栏是 aside,aside 不一定是侧边栏。
  • article: 文章主体部分,因为主体部分具有明确的独立性,所以可以用 article 来包裹。
  • hgroup, h1, h2: hgroup 是标题组,h1 是一级标题,h2 是二级标题。
<hgroup>
  <h1>World Wide Web </h1>
  <h2>From Wikipedia, the free encyclopedia</h2> 
</hgroup>
  • abbr: 表示缩写。
<abbr title="World Wide Web">WWW</abbr>
  • strong: 表示这个词很重要
<strong>The World Wide Web (WWW)</strong>, also called <strong>the Web</strong>
  • figure, figcaption: 这种出现在文中的图片,不仅仅是一个 img 标签,它和下面的文字组成了一个 figure 的语法现象,用于表示与主文章相关的图像、照片等流内容。
<figure>
  <img src="https://.....440px-NeXTcube_first_webserver.JPG"/>
  <figcaption>The NeXT Computer used by Tim Berners-Lee at CERN.</figcaption>
</figure> 

html语义化3

  • nav,ol,ul: 这里的目录链接到文章的各个章节,我们可以使用 nav 标签。因为这里的目录顺序不可随意变化,所以我们 这里使用多级的 ol 结构。
<nav>
  <h2>Contents</h2>
  <ol>
    <li><a href="...">History</a></li>
    <li><a href="...">Function</a>
      <ol>
        <li><a href="...">Linking</a></li>
        <li><a href="...">Dynamic updates of web pages</a></li>
        ...
      </ol>
    </li>
    ...
  </ol>
</nav>

html语义化2

  • pre, samp: 这是一段 HTTP 协议的内容描述,因为这段内容的换行是非常严格的,所以我们不需要浏览器帮我们做自动换行,因此我们使用了 pre 标签,表示这部分内容是预先 排版过的,不需要浏览器进行排版。又因为这是一段计算机程序的示例输出,所以我们可以使用 samp 标签。
<pre>
  <samp> GET /home.html HTTP/1.1 Host: www.example.org </samp>
</pre> 

另外还有,

  • header: 如其名,通常出现在前部,表示导航或者介绍性的内容。
  • footer: 通常出现在尾部,包含一些作者信息、相关链接、版权信息等。
  • time: 表示时间的。
    等等

angular4 路由基础

路由相关对象

1 Routes
路由配置,保存着哪个url对应展示哪个组件,以及再哪个RouterOutlet中展示组件。
2 RouterOutlet
在html中标记路由内容呈现位置的占位符指令。
3 Router
负责在运行时执行路由的对象,可以通过调用其navigate()和navigateByUrl()方法来导航到一个指定的路由。
4 RouterLink
在html中声明路由导航用的指令。
5 ActivatedRoute
当前激活的路由对象,保存着当前路由的信息,如路由地址,路由参数等。

例子:
1 app-routing.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { ProductComponent } from './product/product.component';
import { Code404Component } from './code404/code404.component';

const routes: Routes = [
  {path: '', component: HomeComponent},
  {path: 'product', component: ProductComponent},
  {path: '**', component: Code404Component}
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
  providers: []
})
export class AppRoutingModule {}

2 在app.module.ts中导入AppRoutingModule模块

3 app.component.html
<a [routerLink]="['/']">主页
<a [routerLink]="['/product']">商品详情s
<button (click)="toProductDetails()">商品详情

4 app.component.ts

import { Component } from '@angular/core';
import { router } from '@angular/router';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'app';

  constructor(private router: Router){}

  toProductDetails() {
    this.router.navigate(['/product']);
  }
}

路由参数

1 传递参数
<a [routerLink]="['/product']" [queryParams]="{id: 1}">商品详情
接受参数
constructor(private routeInfo: ActivatedRoute){}
this.productId = this.routeInfo.snapshot.queryParams['id'];
2 在路由配置中携带参数
const routes: Routes = [
{path: '', component: HomeComponent},
{path: 'product/:id', component: ProductComponent},
{path: '**', component: Code404Component}
];
修改路由链接的参数来传递数据
<a [routerLink]="['/product', 1]">商品详情 或者 this.router.navigate(['/product', 1]);
this.productId = this.routeInfo.snapshot.params['id'];

参数快照和参数订阅

snapshot参数快照
this.productId = this.routeInfo.snapshot.params['id'];
subscribe参数订阅
this.routeInfo.params.subscribe((params: Params) => {
this.productId = params['id'];
});

重定向路由

const routes: Routes = [
{path: '', redirectTo: '/home', pathMatch: 'full'},
{path: 'home', component: HomeComponent},
{path: 'product', component: ProductComponent},
{path: '**', component: Code404Component}
];

子路由

路由配置
const routes: Routes = [
{path: '', redirectTo: '/home', pathMatch: 'full'},
{path: 'home', component: HomeComponent},
{path: 'product/:id', component: ProductComponent,
children: [
{path:'', component: ProductDescComponent},
{path:'seller/:id', component: SellerInfoComponent}
]
},
{path: '**', component: Code404Component}
];
使用
ProductComponent对应的html中
<a [routerLink]="['./']">商品描述
<a [routerLink]="['./seller', 99]">销售员信息

辅助路由


{path: 'xxx', component: XxxComponent, outlet: 'aux'}
{path: 'yyy', component: YyyComponent, outlet: 'aux'}

<a [routerLink]="[{outlets: {primary: 'home', aux: 'xxx'}}]">Xxx
<a [routerLink]="['/product', {outlet: {aux: 'yyy'}}]">Yyy

路由守卫

通常当需要满足某些条件时,才可以导航到下一个路由
例如,只有当用户已经登录并且拥有某些权限时才能进入某些路由。或者用户只有在当前路由的组件中填写了满足要求的信息才可以导航到下一个路由。或者当用户未执行保存操作而试图离开当前导航时。

1 CanActivate:处理导航到某路由的情况。
2 CanDeactivate:处理从当前路由离开的情况。
3 Resolve:在路由激活之前获取路由数据。

resolve守卫

使用resolve守卫可以预先在进入路由之前去服务器上读数据,然后把需要的数据读好之后进到这个路由里面,然后立刻就可以把这些数据显示出来。

前端XSS

XSS(cross site scripting 跨站脚本攻击)

1.XSS攻击原理

  程序:
  <div>#{content}</div> 
  数据:
  <script>alert(1)</script>
  结果:
  <div><script>alert(1)</script></div>

script里面的内容由数据变成了程序。

2.XSS攻击的危害

  • 获取页面的数据
  • 获取cookies
  • 劫持前端逻辑
  • 发送请求
    ......

3.XSS攻击分类

  • 反射型:url参数直接注入。攻击代码通过url直接带过来。
  • 存储型:存储到DB后读取时注入。

4.XSS攻击注入点

  • HTML节点内容
    <script></script>
  • HTML属性

    /avatarid=1" onerror="alert(1)"
  • javascript代码
    var data = "hello";alert(1);"";
  • 富文本
    富文本得保留html
    html有xss攻击风险

5.XSS攻击防御

  • 浏览器自带防御
    X-XSS-Protection {0:关闭XSS防御, 1:默认打开XSS防御,1后面指定url,当网站收到xss攻击时会把通知发到指定的url上}
    它能够防御反射型的XSS,出现在html内容和属性中的XSS

  • 自己的防御处理

    • html内容转义<>
    var excapeHtml = function(str) {
      str = str.replace(/</g, '&lt;');
      str = str.replace(/>/g, '&gt;');
      return str;
    }
    • html属性转义"
      var excapeHtmlProperty = function(str) {
        if(!str) return '';
        str = str.replace(/"/g, '&quto;');
        str = str.replace(/'/g, '&#39;');
        return str;
      }

    以上两个函数可以合并成同一个函数使用。

    • javascript代码,转义""或转成json
      escapeForJs = function(str) {
        if (!str) return '';
        str = str.replace(/\\/g, '\\\\');
        str = str.replace(/"/g, '\\"');
        return str;
      }
    
      或者JSON.stringify(data);
    • 富文本
      • 按黑名单过滤
      xssFilter = function(html) {
        if (!html) return '';
        html = html.replace(/<\s*\/?script>\s*/g, '');
        html = html.replace(/javascript:[^'"*]/g, '');
        html = html.replace(/onerror\s*=\s**['"]?[^'"]*['"]?/g, '');
        return html;
      }
    • 按白名单保留部分标签和属性
      cheerio
      xssFilter = function(html) {
        if (!html) return '';
        var cheerio = require('cheerio');
        var $ = cheerio.load(html);
    
        // 白名单
        var whiteList = {
          'img': ['src']
        };
    
        $('*').each(function (index, elem){
          if (whiteList[elem.name]) {
            $(elem).remove();
            return ;
          }
          for (var attr in elem.attribs) {
            if (whiteList[elem.name].indexOf == -1) {
              $(elem).attr(attr, null);
            }
          }
          
        });
        return $.html();
      }

    可以安装别人做好的白名单
    npm install xss

6.CSP(content security policy 内容安全策略)
用于指定哪些内容可以执行xss

Tips

  • 在线阅读github项目代码
    用这种方式可以直接把Chrome变成一个在线IDE。同时npm包也都自动准备好了,直接可以运行。只要把github地址改成 https://stackblitz.com/github 开头就可以了。

github tag打版

如果有一个仓库中有两个分支app和app-im
切换到app分支

  • git tag v1.0.0 -m "app的1.0.0版本"
  • git push origin v1.0.0

然后切换到app-im分支

  • git tag v2.0.0-app-im -m "app-im的2.0.0版本"
  • git push origin v2.0.0-app-im

这样就可以啦

附删除tag命令

  • 删除远程tag:git push origin --delete Tag名(上面自己取得名字)
  • 删除本地tag:git tag -d Tag名(上面自己取得名字)

i18n搭建使用

1 环境搭建
安装angular-translate插件
bower install --save angular-translate
在模块中引用 ‘pascalprecht.translate’
在app.config中配置,使用$translateProvider。具体内容如下:

app.config(function ($translateProvider) {
	//设置初始语言(本文中,zh为中文,en为英文)
  $translateProvider.preferredLanguage('zh');
	//自定义导入json数据
  $translateProvider.useLoader('asyncLoader');
});

我的json数据有2个,
zh对应

{
  "vocabulary_entry_1": "预约挂号",
  "vocabulary_entry_2": "当日挂号",
  "vocabulary_entry_3": "中文",
  "vocabulary_entry_4": "英文",
  "vocabulary_entry_5": "车场地图"
}

en对应

{
  "vocabulary_entry_1": "register",
  "vocabulary_entry_2": "today",
  "vocabulary_entry_3": "chinese",
  "vocabulary_entry_4": "english",
  "vocabulary_entry_5": "Map"
}

首先,通过第一层service拿到http请求的json数据

app.factory('i18nFactory', function($http, $q) {
  var i18nHttp = {};

  i18nHttp.getData = function (lang) {
    var param = null;
    if(angular.isUndefined(lang.key)){
      param = lang;
    }else{
      param = lang.key;
    }
    var q = $q.defer();
    var params = {lang: param};
    $http.get('/i18n',{params: params}).success(function (response) {
      q.resolve(response);
    }).error(function () {
      q.reject('error');
    });
    return q.promise;
  };

  return i18nHttp;
});

然后,通过第二层把处理后的数据的promise传到config中,(注意,config不能注入service)

app.service('asyncLoader', function(i18nFactory, $q) {
  return function(lang) {
    var deferred = $q.defer();
    i18nFactory.getData(lang).then(
      // 通讯成功的处理
      function(response){
        //在这里可以对返回的数据集做一定的处理,再交由controller进行处理
        response.status = true;
        deferred.resolve(response);
      },
      // 通讯失败的处理
      function(error){
        // 可以先对失败的数据集做处理,再交由controller进行处理
        error.status = false;
        deferred.reject(error);
      });
    //返回promise对象,交由controller继续处理成功、失败的业务回调
    return deferred.promise;
  }
});

2 使用方法
最常用的使用方法是在html中通过filter的方式(其他方式可以查看文档):
如果是固定写在html中的,如

预约挂号
,可以改为
{{'vocabulary_entry_3' | translate}}
,其中vocabulary_entry_3为词条名。
如果是$scope绑定的变量,如{{menu.name }},可以改为{{menu.name | translate}}

Demo可以在app-dev-i8n分支查看。语言切换按钮在右上角。

javascript-原型

原型和原型链

知识点

  • 构造函数
function Foo(name, age) {
  this.name = name;
  this.age = age;
  this.class = 'class-1';
  // return this // 默认有这一行
}

let f = new Foo('zhangshan', 20);
  • 构造函数-扩展

    • var a = {} 其实是var a = new Object()的语法糖
    • var a = [] 其实是var a = new Array()的语法糖
    • function Foo(){...} 其实是var Foo = new Function(...)的语法糖
    • 使用前面的方式来写
    • 使用instanceof判断一个函数是否是一个变量的构造函数
  • 原型规则和示例
    原型

    • 所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性(除了"null")
    var obj = {}; obj.a = 100;
    var arr = []; arr.a = 100;
    function fn() {}
    fn.a = 100;
    • 所有的引用类型(数组、对象、函数),都有一个__proto__属性,属性值是一个普通的对象(__proto__隐式原型)
    • 所有函数,都有一个prototype属性,属性值是一个普通的对象(prototype显式原型)
    • 所有的引用类型(数组、对象、函数),__proto__属性值指向它的构造函数的prototype属性值
    var obj = {};
    console.log(obj.__proto__ === Object.prototype)
    // true
    • 当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的__proto__(即它的构造函数的prototype)中寻找
    function Foo(name, age) {
      this.name = name;
    }
    Foo.prototype.alertName = function () {
      alert(this.name);
    }
    
    var f = new Foo('zhangsan');
    f.printName = function() {
      console.log(this.name);
    }
    
    f.printName();
    f.alertName();
    
    // 打印出zhangsan,alert出zhangsan
    // var f = new Foo('zhangsan')的时候f有一个隐式原型__proto__,__proto__指向Foo的prototype,所以f.alertName()的时候会找到Foo.prototype.alertName
    
    // 循环对象自身的属性
    for (let item in f) {
      // 高级浏览器已经再for in 中屏蔽了来自原型的属性,但是这里建议还是加上这个判断,保证程序的健壮性
      if (f.hasOwnProperty(item)) {
        console.log(item);
      }
    }
    // 结果:name printName
  • 原型链
    原型链
    原型链

    function Foo(name, age) {
      this.name = name;
    }
    Foo.prototype.alertName = function () {
      alert(this.name);
    }
    
    var f = new Foo('zhangsan');
    f.printName = function() {
      console.log(this.name);
    }
    
    f.toString(); // 要去f.__proto__.__proto__中查找
  • instanceof

    • 用于判断 引用类型 属于哪个 构造函数 的方法
    • f instanceof Foo的判断逻辑是:
      • f的__proto__一层一层往上,能否对应到Foo.prototype
      • 再试着判断f instanceof Object

如何准确判断一个变量是数组类型

var arr = [];
arr instanceof Array // true
typeof arr // object, typeof是无法判断是否是数组的

写一个原型链继承的例子

// 1
function Animal() {
  this.eat = function() {
    console.log('animal eat');
  }
}

function Dog() {
  this.bark = function() {
    console.log('dog bark');
  }
}

Dog.prototype = new Animal();
var hashiqi = new Dog();

// 2
function Elem(id) {
  this.elem = document.getElementById(id);
}

Elem.prototype.html = function(val) {
  var elem = this.elem;
  if (val) {
    elem.innerHtml = val;
    return this;  // 为了链式操作
  } else {
    return elem.innerHtml;
  }
}

Elem.prototype.on = function(type, fn) {
  var elem = this.elem;
  elem.addEventListener(type, fn);
}

var div1 = new Elem('div1');
div1.html('hello world').on('click', () => {
  alert('clicked');
});

描述new一个对象的过程

  • 创建一个新对象
  • this指向这个新对象
  • 执行代码,即对this赋值
  • 返回this

闭包

  • 闭包的使用场景
    • 函数作为返回值
    • 函数作为参数传递
function F1() {
  var a = 100;
  return function() {
    console.log(a); // 自由变量,父作用域寻找(定义时的作用域寻找,不是执行的作用域)
  }
}
var f1 = F1();
var a = 200;
f1(); // result: 100

// 1. 函数作为返回值
// 2. 函数作为参数传递
function F1() {
  var a = 100;
  return function() {
    console.log(a); 
  }
}
var f1 = F1();
function F2(fn) {
  var a = 200;
  fn();
}
F2(f1);  // result: 100

创建10个标签,点击的时候弹出来对应的序号

for (let i = 0; i < 10; i++) {
  (
    function (i) {
      let a = document.createElement('a');
      a.innerHTML = i + '<br>';
      a.addEventListener('click', function (e) {
        e.preventDefault();
        alert(i);
      }); 
      document.body.appendChild(a);
    }
  )(i)
}

实际开发中的闭包应用

// 闭包实际应用中主要用于封装变量,收敛权限
function isFirstLoad() {
  var list = [];
  return function(id) {
    if (list.indexOf(id) >= 0) {
      return false;
    } else {
      list.push(id);
      return true;
    }
  }
}
var firstLoad = isFirstLoad();
firstLoad(10);  // true
firstLoad(10);  // false
firstLoad(20);  // true

响应式的基本原理

Object.defineProperty

学习自剖析 Vue.js 内部运行机制

在vue2.x中,是基于Object.defineProperty来实现响应式的。对于Object.defineProperty,可以参考具体可以参考 MDN 文档
简单的来说Object.defineProperty的使用方法如下:

/*
    obj: 目标对象
    prop: 需要操作的目标对象的属性名
    descriptor: 描述符
    
    return value 传入对象
*/
Object.defineProperty(obj, prop, descriptor)

这里,descriptor用到的属性包括以下四个:

  • enumerable,属性是否可枚举,默认 false。
  • configurable,属性是否可以被修改或者删除,默认 false。
  • get,获取属性的方法。
  • set,设置属性的方法。

下面来简单的实现一下:

function defineReactive(obj, key, val) {
  Object.defineProperty(obj, key, {
    configurable: true,
    enumerable: true,
    get: function reactiveGetter() { return val;},
    set: function reactiveSetter(newVal){
      if (newVal === val) return;
      // 渲染视图函数(newVal)
    }
  });
}

function observer(value) {
  if (!value || typeof value !== 'object') {
    return;
  }
  Object.keys(value).forEach(key => {
    defineReactive(value, key, value[key])
  })
}
class Vue {
  constructor(obj) {
    this._data = obj.data;
    observer(this._data);
  }
}

let o = new Vue({data: {a: '123'}});
o._data.a = '345';

proxy

01源码解读:从jsx到javascript

一切从一个问题开始:为什么在我们写自定义组件的时候,组件名不能以小写字母开头

  1. 首先来看最简单的jsx到javascript通过Babel的转换
// 转换前
<div key="aaa" className="content">123</div>

// 转换后
"use strict";
React.createElement("div", {
  key: "aaa",
  className: "content"
}, "123");

// 可以看出第一位是标签名,第二位是标签内的属性,第三位是标签的内容(children)

// 如果我们定义一个组件会发生什么呢?
// 转换前
const Abc = () => {
	return <div>component</div>
}

<Abc key="aaa" className="content">
  <span>1</span>
  <span>2</span>
</Abc>

// 转换后
"use strict";

var Abc = function Abc() {
  return React.createElement("div", null, "component");
};

React.createElement(Abc, {
  key: "aaa",
  className: "content"
}, React.createElement("span", null, "1"), React.createElement("span", null, "2"));

// 可以发现,第一位不再是标签名,而是一个变量
// 如果此时把组件名第一位改成小写的话,第一位又会变成标签名,而标签中是不存在我们自定义的标签名的,所以自定义组件首字母不能小写

// 转换前
const abc = () => {
	return <div>component</div>
}

<abc key="aaa" className="content">
  <span>1</span>
  <span>2</span>
</abc>

// 转换后
"use strict";
var abc = function abc() {
  return React.createElement("div", null, "component");
};

React.createElement("abc", {
  key: "aaa",
  className: "content"
}, React.createElement("span", null, "1"), React.createElement("span", null, "2"));
  1. reactElement
// createElement接收type,config,children三个参数,返回一个对象
export function createElement(type, config, children) {
  const props = {};

  // 如果config存在,处理config,除了key和ref和self和source,其他都放到props中

  // 把children的内容放到props.children中

  // 如果存在默认值把默认值存到props中
  
  // 返回一个对象
  return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props,
  );
}


const ReactElement = function(type, key, ref, self, source, owner, props) {
  const element = {
    // This tag allows us to uniquely identify this as a React Element
    // react.element类型
    $$typeof: REACT_ELEMENT_TYPE,

    // Built-in properties that belong on the element
    type: type,
    key: key,
    ref: ref,
    props: props,

    // Record the component responsible for creating this element.
    _owner: owner,
  };
  ...
  return element;
};

// ReactElement返回一个对象,里面包括以下信息:
// 1. type类型,用于判断如何创建节点
// 2. key和ref这些特殊信息
// 3. props新的属性内容
// 4. $$typeof用于确定是否属于ReactElement

css基础

HTML

html5有什么变化

  • 新的语义化元素
  • 表单增强
  • 新的API(离线、音视频、图形、实时通信、本地存储、设备能力)
  • 分类和嵌套变更

em和i有什么区别

  • em是语义化标签,,表强调
  • i是纯样式的标签,表斜体
  • html5中i一般用作图标

语义化的意义是什么

  • 开发者容易理解
  • 机器容易理解结构(搜索、读屏软件)
  • 有助于SEO
  • semantic microdata

哪些元素可以自闭合

  • 表单元素input
  • 图片 img
  • br hr
  • meta link

html和dom的关系

  • html是‘死’的
  • dom是由html解析而来,是活的
  • js可以维护dom

property和attribute的区别

  • attribute是‘死’的
  • property是‘活’的
  • P.S. 调试的console中$0可以表示elements中选择的元素
<input type="text" value="1">

$0.value  =>  1
$0.value = "2"
$0.value  =>  2
$0.getAttribute("value")  =>  1

form的作用有哪些

  • 直接提交表单
  • 使用submit/reset按钮
  • 便于浏览器保存表单
  • 第三方库可以整体提取值
  • 第三方库可以进行表单验证

h5o - HTML5 Outliner

css基础

选择器的分类

  • 元素选择器 a{}
  • 伪元素选择器 ::before{}
  • 类选择器 .link{}
  • 属性选择器 [type=radio]{}
  • 伪类选择器 :hover{}
  • id选择器 #id{}
  • 组合选择器 [type=checkbox] + label {}
  • 否定选择器 :not(.link){}
  • 通用选择器 *{}

选择器权重

  • !important优先级最高
  • 元素属性 优先级高
  • 相同权重 后写的生效

非布局样式

  • 字体、字重、颜色、大小、行高

    字体族

    • serif sans-serif monospace cursive fantasy

    多字体fallback

    网络字体、自定义字体

    @font-face {
      font-family: "customFont";
      src: url("./IndieFlower.ttf");
    }
    .costom-font {
      font-family: customFont;
    }
    

    iconfont

    阿里巴巴矢量图标库

    行高

    图片3px缝隙问题:img是一个inline元素,会按照baseline对齐,基线对齐意味着在基线和底线之间还是会有一个空隙的。偏差的大小随字体的大小而定。一般来说12px的字体大小偏差在3px左右。解决方法:第一种方法:按底线对齐(vertical-align: bottom),第二种方法:display: block

  • 背景、边框

  • 滚动、换行

    文字折行

    • overflow-wrap(word-wrap)通用换行控制:是否保留单词
    • word-break 针对多字节文字:中文句子也是单词
    • white-space 空白处是否断行
  • 粗体、斜体、下划线

  • 其他

css样式的优先级

  • 计算权重确定
  • !important
  • 内联样式
  • 后写的优先级高

雪碧图的作用

  • 减少http请求数 提高加载性能
  • 有一些情况下可以减少图片大小

自定义字体的使用场景

  • 宣传/品牌/banner等固定文案
  • 字体图标

base64的使用

  • 减少http请求
  • 适用于小图片
  • base64的体积约为原图的三分之四

伪类和伪元素的区别

  • 伪类表状态
  • 伪元素是真的有元素
  • 前者单冒号,后者双冒号

如何美化checkbox

  • label[for]和id
  • 隐藏原生input
  • :checked + label

CSS布局

  • flexbox:弹性盒子、盒子本来就是并列的、指定宽度即可
  • float
  • 响应式设计和布局
    • 主要方法:
      • 隐藏 + 折行 + 自适应空间
      • rem/viewport/media query

display: inline-block的间隙

  • 原因:字符间距
  • 解决:消灭字符或者消灭间距(父元素的font-size:0,再把子元素的font-size设回来。)

CSS效果

如何产生不占空间的边框

  • box-shadow(不给模糊值即可)
  • outline

如何实现半圆、扇形等图形

  • border-radius组合

CSS预处理器

  • less
  • sass

嵌套

less与sass完全一样

.wrapper {
  .content {
    &:hover {
      ...
    }
  }
}
使用&,上面的样式等价于
.wrapper .content:hover {...}

变量

  • less
@fontSize: 12px;
@bgColor: red;

.wrapper {
  background: lighten(@bgColor, 40%);

  .nav {
    font-size: @fontSize;
  }

  .content {
    font-size: @fontSize + 2px;
  }
}
  • sass
$fontSize: 12px;
$bgColor: red;

.wrapper {
  background: lighten($bgColor, 40%);

  .nav {
    font-size: $fontSize;
  }

  .content {
    font-size: $fontSize + 2px;
  }
}

mixin(代码复用)

  • less
@fontSize: 12px;
@bgColor: red;

.block(@fontSize) {
  font-size: @fontSize;
  border: 1px solid #ccc;
}

.wrapper {

  .nav {
    .block(@fontSize);
  }

  .content {
    .block(@fontSize + 2px);
  }
}

编译成css后 =>

.wrapper .nav {
  font-size: 12px;
  border: 1px solid #ccc;
}
.wrapper .content {
  font-size: 14px;
  border: 1px solid #ccc;
}

  • sass {
$fontSize: 12px;
$bgColor: red;

@mixin block($fontSize) {
  font-size: $fontSize;
  border: 1px solid #ccc;
}

.wrapper {

  .nav {
    @include block($fontSize);
  }

  .content {
    @include block($fontSize + 2px);
  }
}

extend

比mixin产生的代码更少,但是如果需要参数的话还是需要用mixin

  • less
@fontSize: 12px;
@bgColor: red;

.block {
  font-size: @fontSize;
  border: 1px solid #ccc;
}

.wrapper {

  .nav:extend(.block) {
    color: #333;
  }

  .content:extend(.block) {
    // 等价写法 &:extend(.block)
  }
}

编译成css后 =>

.block,
.wrapper .nav,
.wrapper .content {
  font-size: 12px;
  border: 1px solid #ccc;
}
.wrapper .nav {
  color: #333;
}

  • sass {
$fontSize: 12px;
$bgColor: red;

.block {
  font-size: $fontSize;
  border: 1px solid #ccc;
}

.wrapper {

  .nav {
    @extend .block;
  }

  .content {
    @extend .block;
  }
}

loop

  • less
.gen-col(@n) when (@n > 0) {
  .col-@{n} {
    width: 1000px/12*@n;
  }
  .gen-col(@n - 1);
}

.gen-col(12);

编译成css后 =>

.col-12 {
  width: 1000px;
}
.col-11 {
  width: 916.66666667px;
}
...
.col-1 {
  width: 83.33333333;
}

  • sass
@for $i from 1 through 12 {
  .col-#{$i} {
    width: 1000px/12*$i;
  }
}

预处理器中css模块化

@import "./import-variable" //如果文件中有@fontSize,下面两个模块也可以使用@fontSize
@import "./import-module1"
@import "./import-module2"

css预处理器框架

  • sass-Compass
  • less-Lesshat/EST
  • 提供现成的mixin
  • 类似js类库 封装常用功能

常见的css预处理器

  • less(node.js)
  • sass(ruby 有node版本)

预处理器的作用

  • 帮助更好的组织css代码
  • 提高代码复用率
  • 提升可维护性

预处理器主要的能力

  • 嵌套:反映层级和约束
  • 变量和计算:减少重复代码
  • extend和mixin:代码片段
  • 循环:适用于复杂有规律的样式
  • import css文件模块化

预处理器的优缺点

  • 优点: 提高代码服用率和可维护性
  • 缺点: 需要引入编译过程

自己有时候要用的,有时候又想不起来的

  • github里看改动前后代码时,由于空行问题造成的全部更改,可以在链接后面加?w=1

  • 折行显示
    word-wrap: break-word;
    word-break: break-all;
    white-space: normal;

  • 省略显示
    white-space:nowrap;
    overflow:hidden;
    text-overflow:ellipsis;

  • 图片与文字对齐

    <p><img src='...'/>aaaa</p>
    img { vertical-align:text-top; }

2. javascript类型

原始(Primitive)类型

  • boolean
  • number
  • string
  • null
  • undefined
  • symbol

首先原始类型存储的都是值,是没有函数可以调用的,比如 undefined.toString()

此时你肯定会有疑问,这不对呀,明明 '1'.toString() 是可以使用的。其实在这种情况下,'1' 已经不是原始类型了,而是被强制转换成了 String 类型也就是对象类型,所以可以调用 toString 函数。

另外对于 null 来说,很多人会认为他是个对象类型,其实这是错误的。虽然 typeof null 会输出 object,但是这只是 JS 存在的一个悠久 Bug。在 JS 的最初版本中使用的是 32 位系统,为了性能考虑使用低位存储变量的类型信息,000 开头代表是对象,然而 null 表示为全零,所以将它错误的判断为 object 。虽然现在的内部类型判断代码已经改变了,但是对于这个 Bug 却是一直流传下来。

对象(Object)类型

在 JS 中,除了原始类型那么其他的都是对象类型了。对象类型和原始类型不同的是,原始类型存储的是值,对象类型存储的是地址(指针)。当你创建了一个对象类型的时候,计算机会在内存中帮我们开辟一个空间来存放值,但是我们需要找到这个空间,这个空间会拥有一个地址(指针)。

[基本数据类型]
// => 0 -13 13.2 
// 数字类型中有一个特殊的值NaN(not a number代表不是一个有效的数字,但是属于number类型)
var n = 13;

// => "" '13' "{}" js中所有用单引号和双引号包裹起来的都是字符串,一个字符串由0到多个字符组成

// => 布尔类型 true真 false假

[引用数据类型]
// => 普通对象
let o = {name: 'cbj', age: 19};

// 数组对象
let arr = [1, 2, 3];

// 正则
let reg = /-?(\d|([1-9]\d+))(\.\d+)?/g

[Symbol]
// 创建出来的是一个唯一的值
var a = Symbol('abc');
var b = Symbol('abc');
// a==b => false
const PI = Symbol('3.14');

数据类型的详细剖析

number数字类型

  • NaN:not a number 但是它是数字类型的
  • isNaN:检测当前值是否不是有效数字,返回true代表不是有效数字,返回false是有效数字
//=>语法:isNaN([value])
var num=12;
isNaN(num); //->检测num变量存储的值是否为非有效数字 false

isNaN('13') =>false
isNaN('珠峰') =>true
isNaN(true) =>false
isNaN(false) =>false
isNaN(null) =>false
isNaN(undefined) =>true
isNaN({age:9}) =>true
isNaN([12,23]) =>true
isNaN([12]) =>false
isNaN(/^$/) =>true
isNaN(function(){}) =>true

重要:isNaN检测的机制

  1. 首先验证当前要检测的值是否为数字类型的,如果不是,浏览器会默认的把值转换为数字类型
  • 其它基本类型转换为数字:直接使用Number这个方法转换的
  [字符串转数字]
    Number('13') ->13
    Number('13px') ->NaN 如果当前字符串中出现任意一个非有效数字字符,结果则为NaN
    Number('13.5') ->13.5 可以识别小数

  [布尔转数字]
    Number(true) ->1
    Number(false) ->0

  [其它]
    Number(null) ->0
    Number(undefined) ->NaN
  • 把引用数据类型值转换为数字:先把引用值调取toString转换为字符串,然后再把字符串调取Number转换为数字
   [对象]
     ({}).toString() -> '[object Object]' -> NaN

   [数组]
     [12,23].toString() -> '12,23' -> NaN
     [12].toString() -> '12' -> 12

   [正则]
     /^$/.toString() -> '/^$/' -> NaN

    Number('') -> 0
    [].toString() -> ''
    isNaN([]):false
  1. 当前检测的值已经是数字类型,是有效数字返回false,不是返回true(数字类型中只有NaN不是有效数字,其余都是有效数字)

parseInt / parseFloat

  • 等同于Number,也是为了把其它类型的值转换为数字类型
  • 和Number的区别在于字符串转换分析上
  • Number:出现任意非有效数字字符,结果就是NaN
  • parseInt:把一个字符串中的整数部分解析出来,parseFloat是把一个字符串中小数(浮点数)部分解析出来
parseInt('13.5px') => 13
parseFloat('13.5px') => 13.5

parseInt('width:13.5px') =>NaN 从字符串最左边字符开始查找有效数字字符,并且转换为数字,但是一但遇到一个非有效数字字符,查找结束

NaN的比较

NaN==NaN:false NaN和谁都不相等,包括自己

思考题 1:有一个变量num,存储的值不知道,我想检测它是否为一个有效数字,下面方案是否可以

if(Number(num)==NaN){
    alert('num不是有效数字!');
}

NaN和谁都不相等,条件永远不成立(即使num确实不是有效数字,转换的结果确实是NaN,但是NaN!=NaN的)

if(isNaN(num)){
    //=>检测是否为有效数字,只有这一种方案
    alert('num不是有效数字!')
}

思考题 2:为什么 0.1 + 0.2 != 0.3?如何解决这个问题?

先说原因,因为 JS 采用 IEEE 754 双精度版本(64位),并且只要采用 IEEE 754 的语言都有该问题。IEEE 754 双精度版本(64位)将 64 位分为了三段

  • 第一位用来表示符号
  • 接下去的 11 位用来表示指数
  • 其他的位数用来表示有效位,也就是用二进制表示 0.1 中的 10011(0011)

那么这些循环的数字被裁剪了,就会出现精度丢失的问题,也就造成了 0.1 不再是 0.1 了,而是变成了 0.100000000000000002

0.100000000000000002 === 0.1 // true

那么同样的,0.2 在二进制也是无限循环的,被裁剪后也失去了精度变成了 0.200000000000000002

0.200000000000000002 === 0.2 // true

所以这两者相加不等于 0.3 而是 0.300000000000000004

0.1 + 0.2 === 0.30000000000000004 // true

既然 0.1 不是 0.1,那为什么 console.log(0.1) 却是正确的呢?

因为在输入内容的时候,二进制被转换为了十进制,十进制又被转换为了字符串,在这个转换的过程中发生了取近似值的过程,所以打印出来的其实是一个近似值,你也可以通过以下代码来验证

console.log(0.100000000000000002) // 0.1

解决办法:

// 第一种
parseFloat((0.1 + 0.2).toFixed(10)) === 0.3 // true
// 第二种
Math.abs(0.1 + 0.2 - 0.3) <= Number.EPSILON // true

布尔类型

只有两个值:true / false

如何把其它数据类型转换为布尔类型?

  • Boolean
  • !
  • !!
Boolean(1) =>true

!'abc' => 先把其它数据类型转换为布尔类型,然后取反

!!null => 去两次反,等价于没取反,也就剩下转换为布尔类型了

规律:在JS中只有“0/NaN/空字符串/null/undefined”这五个值转换为布尔类型的false,其余都转换为true

null && undefined

都代表空或者没有

  • null:空对象指针
  • undefined:未定义

null一般都是意料之中的没有(通俗理解:一般都是人为手动的先赋值为null,后面的程序中我们会再次给他赋值)

var num = null; //=>null是手动赋值,预示着后面我会把num变量的值进行修改
...
num = 12;

undefined代表的没有一般都不是人为手动控制的,大部分都是浏览器自主为空(后面可以赋值也可以不赋值)

var num; //=>此时变量的值浏览器给分配的就是undefined
...
后面可以赋值也可以不赋值

object对象数据类型

普通对象

  • 由大括号包裹起来的
  • 由零到多组属性名和属性值(键值对)组成

属性是用来描述当前对象特征的,属性名是当前具备这个特征,属性值是对这个特征的描述(专业语法,属性名称为键[key],属性值称为值[value],一组属性名和属性值称为一组键值对)

var obj = {
	name:'aaa',
	age:9
};
// => 对象的操作:对键值对的增删改查
// 语法:对象.属性 / 对象[属性]

// => [获取]
obj.name
obj['name']

// => [增/改]
JS对象中属性名是不允许重复的,是唯一的
obj.name = 'tom'; // => 原有对象中存在name属性,此处属于修改属性值
obj.sex = '男'; //=>原有对象中不存在sex,此处相当于给当前对象新增加一个属性sex
obj['age'] = 28;

// => [删]
// 彻底删除:对象中不存在这个属性了
delete obj['age'];

// 假删除:并没有移除这个属性,只是让当前属性的值为空
obj.sex = null;

/*
在获取属性值的时候,如果当前对象有这个属性名,则可以正常获取到值(哪怕是null),但是如果没有这个属性名,则获取的结果是undefined
obj['friends'] =>undefined
*/

一个对象中的属性名不仅仅是字符串格式的,还有可能是数字格式的

var obj = {
  name: 'abc',
  0: 100
};
obj[0] // => 100
obj['0'] // => 100
obj.0 // => Uncaught SyntaxError: Unexpected number

----
// 当我们存储的属性名不是字符串也不是数字的时候,浏览器会把这个值转换为字符串(toString),然后再进行存储

obj[{}]=300;  // =>先把({}).toString()后的结果作为对象的属性名存储进来 obj['[object Object]']=300

obj[{}] // =>获取的时候也是先把对象转换为字符串'[object Object]',然后获取之前存储的300

----
数组对象(对象由键值对组成的)
var oo = {
  a: 12
};
var ary = [12,23]; //=>12和23都是属性值,属性名呢?

通过观察结果,我们发现数组对象的属性名是数字(我们把数字属性名称为当前对象的索引)
ary[0]
ary['0']
ary.0  =>报错

浅分析js的运行机制

  1. 当浏览器(他的内核/引擎)渲染和解析js的时候,会提供一个供js代码运行的环境,我们把这个环境称之为全局作用域(global/window scope)
  2. 代码自上而下执行(之前还有一个变量提升阶段,函数的话会有形参赋值。但是如果用let,则没有变量提升阶段),基本数据类型的值会存储在当前作用于下
  • var a = 12;
  • 1)首先开辟一个空间存储12
  • 2)在当前作用域中声明一个变量a (var a)
  • 3)让声明的变量和存储的12进行关联(把存储的12赋值给a =>赋值操作叫做定义)
  • 基本数据类型(也叫作值类型),是按照值来操作的:把原有的值复制一份,放到新的空间或者位置上,和原来的值没有关系
  • =>引用数据类型的值不能直接存储到当前的作用域下(因为可能存储的内容过于复杂),我们需要先开辟一个新的空间(理解为仓库),把内容存储到这个空间中
  • var obj1 = {n: 100};
  • 1)首先开辟一个新的内存空间,把对象中的键值对依次存储起来(为了保证后面可以找到这个空间,此空间有一个16进制的地址)
  • 2)声明一个变量
  • 3)让变量和空间地址关联在一起(把空间地址赋值给变量)
  • 引用类型不是按照值来操作,它操作的是空间的引用地址:把原来空间的地址赋值给新的变量,但是原来的空间没有被克隆,还是一个空间,这样就会出现多个变量关联的是相同的空间,相互之间就会存在影响了
/*
* 1、形成一个全局作用域(栈内存)
* 2、代码自上而下执行
*   1.首先开辟一个新的堆内存(AAAFFF111),把键值对存储到堆内存中
*     n:10
*     m:obj.n * 10 => obj.n 此时堆内存信息还没有存储完成,空间的地址还没有给obj,此时的obj是undefined,obj.n<=>undefined.n

   var obj = {
       n: 10,
       m: obj.n * 10 //=>Uncaught TypeError: Cannot read property 'n' of undefined
   };
   console.log(obj.m);
*/
var obj = {
   n: 10
};
obj.m = obj.n * 10;
console.log(obj.m); //=>100


var ary1 = [3, 4];
var ary2 = ary1;
ary2[0] = 1;
ary2 = [4, 5];
ary2[1] = 2;
ary1[1] = 0;
console.log(ary1, ary2);
// => [1, 0] [4, 2]

BAT面试题:

var num = parseInt('width:35.5px');
if(num==35.5){
	alert(0);
}else if(num==35){
	alert(1);
}else if(num==NaN){
	alert(2);
}else if(typeof num=='number'){
    //=>先算typeof num
    //=>在做比较
	alert(3);//=>alert输出的都是字符串格式的 '3'
}else{
    alert(4);
}

typeof vs instanceof

在JS中用来检测数据类型的方式:

  • typeof
  • instanceof
  • constructor
  • Object.prototype.toString.call()

typeof

语法:typeof [value] =>检测value的数据类型

返回值:使用typeof检测出来的结果是一个字符串,字符串中包含着对应的数据类型,例如:"number"/"string"/"boolen"/"undefined"/"object"/"function"

typeof null =>"object" 因为null代表空对象指针(没有指向任何的内存空间)

typeof 对于原始类型来说,除了 null 都可以显示正确的类型

typeof 1 // 'number'
typeof '1' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol() // 'symbol'
typeof (function(){}) // 'function'

typeof 对于对象来说,除了函数都会显示 object,所以说 typeof 并不能准确判断变量到底是什么类型

typeof [] // 'object'
typeof {} // 'object'
typeof console.log // 'function'

instanceof

如果我们想判断一个对象的正确类型,这时候可以考虑使用 instanceof,因为内部机制是通过原型链来判断的。

const Person = function() {}
const p1 = new Person()
p1 instanceof Person // true

var str = 'hello world'
str instanceof String // false

var str1 = new String('hello world')
str1 instanceof String // true

对于原始类型来说,你想直接通过 instanceof 来判断类型是不行的,当然我们还是有办法让 instanceof 判断原始类型的

class PrimitiveString {
  static [Symbol.hasInstance](x) {
    return typeof x === 'string'
  }
}
console.log('hello world' instanceof PrimitiveString) // true

你可能不知道 Symbol.hasInstance 是什么东西,其实就是一个能让我们自定义 instanceof 行为的东西,以上代码等同于 typeof 'hello world' === 'string',所以结果自然是 true 了。这其实也侧面反映了一个问题, instanceof 也不是百分之百可信的。

函数

  • 在JS中,函数就是一个方法(一个功能体),基于函数一般都是为了实现某个功能

函数诞生的目的就是为了实现封装:把实现一个功能的代码封装到一个函数中,后期想要实现这个功能,只需要把函数执行即可,不必要再次编写重复的代码,起到了**低耦合高内聚(减少页面中的冗余代码,提高代码的重复使用率)**的作用

function fn(){
	var total=10;
	total+=10;
	total/=2;
	total=total.toFixed(2);
	console.log(total);
}
fn();
fn();
...
想用多少次,我们就执行多少次函数即可

=====
ES3标准中:
//=>创建函数
function 函数名([参数]){
  函数体:实现功能的JS代码
}
//=>函数执行
函数名(); 

=====
ES6标准中创建箭头函数:
let 函数名(变量名)=([参数])=>{
  函数体
}
函数名();

let fn=()=>{
  let total=10;
  ...
};
fn();

函数作为引用数据类型中的一种,它也是按照引用地址来操作的,接下来我们学习一下函数的运行机制

function fn(){
	var total=10;
	total+=10;
	total=total.toFixed(2);
	console.log(total);
}
fn();

【创建函数】
1. 函数也是引用类型,首先会开辟一个新的堆内存,把函数体中的代码当做“字符串”存储到内存中(对象向内存中存储的是键值对)
2. 把开辟的堆内存地址赋值给函数名(变量名)

此时我们输出fn(切记不是fn())代表当前函数本身
如果我们执行fn(),这是把函数执行
所以是否加小括号是两种不同本质的操作

【函数执行】
目的:把之前存储到堆内存中的代码字符串变为真正的JS代码自上而下执行,从而实现应有的功能

1.函数执行,首先会形成一个私有的作用域(一个供代码执行的环境,也是一个栈内存)
2.把之前在堆内存中存储的字符串复制一份过来,变为真正的JS代码,在新开辟的作用域中自上而下执行 

函数中的参数

参数是函数的入口:当我们在函数中封装一个功能,发现一些原材料不确定,需要执行函数的时候用户传递进来才可以,此时我们就基于参数的机制,提供出入口即可

//=>此处的参数叫做形参:入口,形参是变量(n/m就是变量)
function sum(n,m){
	//=>n和m分别对应要求和的两个数字
	var total = 0;
	total = n + m;
	console.log(total);
}

//=>此处函数执行传递的值是实参:实参是具体的数据值
sum(10,20);  //=>n=10 m=20
sum(10); //=>n=10 m=undefined
sum(); //=>n和m都是undefined
sum(10,20,30); //=>n=10 m=20  30没有形参变量接收

JS中数据类型转换汇总

真实项目中,根据需求,我们往往需要把数据类型之间进行转换

把其它数据类型转换为number类型

1.发生的情况

  • isNaN检测的时候:当检测的值不是数字类型,浏览器会自己调用Number方法把它先转换为数字,然后再检测是否为非有效数字
isNaN('3') =>false
//  Number('3')->3
//  isNaN(3)->false

isNaN('3px') =>true
//  Number('3px')->NaN
//  isNaN(NaN)->true
  • 基于parseInt/parseFloat/Number去手动转换为数字类型
  • 数学运算:+ - * / %,但是“+”不仅仅是数学运算,还可能是字符串拼接
'3'-1 =>2
//  Number('3')->3
//  3-1->2

'3px'-1 =>NaN

var i='3';
i=i+1; =>'31' //字符串拼接
i++; =>34  //i++就是单纯的数学运算,已经摒弃掉字符串拼接的规则
  • 在基于“==”比较的时候,有时候也会把其它值转换为数字类型
  • ...

2.转换规律

原始值 转换目标 结果
string 数字 '1' => 1, 'a' => NaN
数组 数字 [] => 0, [1] => 1, 其他情况NaN
null 数字 0
除了数组的引用类型 数字 NaN
symbol 数字 抛错
//=>转换的方法:Number(浏览器自行转换都是基于这个方法完成的)

【把字符串转换为数字】
只要遇到一个非有效数字字符,结果就是NaN
'' -> 0
' ' -> 0 空格(Space)
'\n' -> 0 换行符(Enter)
'\t' -> 0 制表符(Tab)


【把布尔转换为数字】
true -> 1
false -> 0

【把没有转换为数字】
null -> 0
undefined -> NaN

【把引用类型值转换为数字】
首先都先转换为字符串(toString),然后再转换为数字(Number)

把其它类型值转换为字符串

1.发生的情况

  • 基于alert/confirm/prompt/document.write等方法输出内容的时候,会把输出的值转换为字符串,然后再输出
alert(1) =>'1'
  • 基于“+”进行字符串拼接的时候
  • 把引用类型值转换为数字的时候,首先会转换为字符串,然后再转换为数字
  • 给对象设置属性名,如果不是字符串,首先转换为字符串,然后再当做属性存储到对象中(对象的属性只能是数字或者字符串)
  • 手动调用toString/toFixed/join/String等方法的时候,也是为了转换为字符串
var n=Math.PI;//=>获取圆周率:
n.toFixed(2) =>'3.14'

var ary=[12,23,34];
ary.join('+') =>'12+23+34'
  • ...

2.转换规律

原始值 转换目标 结果
number 字符串 5 => '5'
boolean、函数、symbol 字符串 'true'
数组 字符串 [1, 2] => '1,2'
对象 字符串 '[object object]'
//=>调用的方法:toString

【除了对象,都是你理解的转换结果】
1 ->'1'
NaN ->'NaN'
null ->'null'
[] ->''
[13] ->'13'
[12,23] ->'12,23'
...

【对象】
{name:'xxx'} ->'[object Object]'
{} ->'[object Object]'
不管是啥样的普通对象,最后结果都一样

把其它值转换为布尔类型

1.发生的情况

  • 基于!/!!/Boolean等方法转换
  • 条件判断中的条件最后都会转换为布尔类型
  • ...

2.转换的规律
只有“0/NaN/''/null/undefined”五个值转换为布尔的false,其余都是转换为true

原始值 转换目标 结果
number 布尔值 除了0,NaN,都为true
string 布尔值 除了'',都为true
undefined 布尔值 false
null 布尔值 false
引用类型 布尔值 true

特殊情况:数学运算和字符串拼接 “+”

//=>当表达式中出现字符串,就是字符串拼接,否则就是数学运算

1+true // =>2 数学运算
'1'+true // =>'1true' 字符串拼接

[12]+10 // =>'1210' 虽然现在没看见字符串,但是引用类型转换为数字,首先会转换为字符串,所以变为了字符串拼接
({})+10 // =>"[object Object]10"
[]+10 // =>"10"

{}+10 // =>10 这个和以上说的没有半毛钱关系,因为它根本就不是数学运算,也不是字符串拼接,它是两部分代码
//  {} 代表一个代码块(块级作用域)
//  +10 才是我们的操作
//  严格写法:{}; +10;

思考题:

12+true+false+null+undefined+[]+'天天'+null+undefined+[]+true
// =>'NaN天天nullundefinedtrue'
/*
12+true ->13
13+false ->13
13+null ->13
13+undefined ->NaN
NaN+[] ->'NaN'
'NaN'+'天天' ->'NaN天天'
...
'NaN天天trueundefined'
'NaN天天trueundefined'+[] ->'NaN天天trueundefined'
...
=>'NaN天天trueundefinedtrue'
*/

特殊情况:“==”在进行比较的时候,如果左右两边的数据类型不一样,则先转换为相同的类型,再进行比较

对象==对象:不一定相等,因为对象操作的是引用地址,地址不相同则不相等

{name:'xxx'}=={name:'xxx'} =>false
[]==[] =>false

var obj1={};
var obj2=obj1;
obj1==obj2 =>true
  • 不同情况的比较,都是把其它值转换为数字,然后再进行比较的
    1. 对象==数字:把对象转换为数字
    1. 对象==布尔:把对象转换为数字,把布尔也转换为数字
    1. 对象==字符串:把对象转换为数字,把字符串也转换为数字
    1. 字符串==数字:字符串转换为数字
    1. 字符串==布尔:都转换为数字
    1. 布尔==数字:把布尔转换为数字
  • 需要特殊记忆:
    1. null==undefined:true
    1. null===undefined:false
  • null&&undefined和其它值都不相等
    1. NaN==NaN:false
  • NaN和谁都不相等包括自己
1 == true // =>true
1 == false // =>false
2 == true // =>false  规律不要混淆,这里是把true变为数字1

[] == false // true  都转换为数字 0==0
![] == false // true  先算![],把数组转换为布尔取反=>false  =>false==false

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.